(SSD1306)M5NanoC6でランダムウォークを高速フーリエ変換して表示して遊ぶ(FFT)
Arduino M5stack ssd1306
x 過去ログをみよ
結果
プログラム
(StampS3ようを完全にそのまま)
//秋月のOLEDとアイテンドウのOLEDのアドレスは3C
//SSD1306_no_vram_graph_fft_c_2_S3_1
//インクルド(SSD1306用)
#include <Arduino.h>
#include <Wire.h>
//FFT関連(1) 開始↓
/***********************************************************
fft.c -- FFT (高速Fourier変換)
***********************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define PI 3.14159265358979323846
/*
関数{\tt fft()}の下請けとして三角関数表を作る.
*/
static void make_sintbl(int n, double sintbl[])
{
int i, n2, n4, n8;
double c, s, dc, ds, t;
n2 = n / 2; n4 = n / 4; n8 = n / 8;
t = sin(PI / n);
dc = 2 * t * t; ds = sqrt(dc * (2 - dc));
t = 2 * dc; c = sintbl[n4] = 1; s = sintbl[0] = 0;
for (i = 1; i < n8; i++) {
c -= dc; dc += t * c;
s += ds; ds -= t * s;
sintbl[i] = s; sintbl[n4 - i] = c;
}
if (n8 != 0) sintbl[n8] = sqrt(0.5);
for (i = 0; i < n4; i++) {
sintbl[n2 - i] = sintbl[i];
sintbl[i + n2] = - sintbl[i];
}
}
/*
関数{\tt fft()}の下請けとしてビット反転表を作る.
*/
static void make_bitrev(int n, int bitrev[])
{
int i, j, k, n2;
n2 = n / 2; i = j = 0;
for ( ; ; ) {
bitrev[i] = j;
if (++i >= n) break;
k = n2;
while (k <= j) { j -= k; k /= 2; }
j += k;
}
}
/*
高速Fourier変換 (Cooley--Tukeyのアルゴリズム).
標本点の数 {\tt n} は2の整数乗に限る.
{\tt x[$k$]} が実部, {\tt y[$k$]} が虚部 ($k = 0$, $1$, $2$,
\ldots, $|{\tt n}| - 1$).
結果は {\tt x[]}, {\tt y[]} に上書きされる.
${\tt n} = 0$ なら表のメモリを解放する.
${\tt n} < 0$ なら逆変換を行う.
前回と異なる $|{\tt n}|$ の値で呼び出すと,
三角関数とビット反転の表を作るために多少余分に時間がかかる.
この表のための記憶領域獲得に失敗すると1を返す (正常終了時
の戻り値は0).
これらの表の記憶領域を解放するには ${\tt n} = 0$ として
呼び出す (このときは {\tt x[]}, {\tt y[]} の値は変わらない).
*/
int fft(int n, double x[], double y[])
{
static int last_n = 0; /* 前回呼出し時の {\tt n} */
static int *bitrev = NULL; /* ビット反転表 */
static double *sintbl = NULL; /* 三角関数表 */
int i, j, k, ik, h, d, k2, n4, inverse;
double t, s, c, dx, dy;
/* 準備 */
if (n < 0) {
n = -n; inverse = 1; /* 逆変換 */
} else inverse = 0;
n4 = n / 4;
if (n != last_n || n == 0) {
last_n = n;
if (sintbl != NULL) free(sintbl);
if (bitrev != NULL) free(bitrev);
if (n == 0) return 0; /* 記憶領域を解放した */
//sintbl = malloc((n - n4) * sizeof(double));
//sintbl = (double *)malloc(10000);
sintbl = (double *)malloc((n - n4) * sizeof(double));
//bitrev = malloc(n * sizeof(int));
//bitrev = (int *)malloc(10000);
bitrev = (int *)malloc(n * sizeof(int));
if (sintbl == NULL || bitrev == NULL) {
fprintf(stderr, "記憶領域不足\n"); return 1;
}
make_sintbl(n, sintbl);
make_bitrev(n, bitrev);
}
for (i = 0; i < n; i++) { /* ビット反転 */
j = bitrev[i];
if (i < j) {
t = x[i]; x[i] = x[j]; x[j] = t;
t = y[i]; y[i] = y[j]; y[j] = t;
}
}
for (k = 1; k < n; k = k2) { /* 変換 */
h = 0; k2 = k + k; d = n / k2;
for (j = 0; j < k; j++) {
c = sintbl[h + n4];
if (inverse) s = - sintbl[h];
else s = sintbl[h];
for (i = j; i < n; i += k2) {
ik = i + k;
dx = s * y[ik] + c * x[ik];
dy = c * y[ik] - s * x[ik];
x[ik] = x[i] - dx; x[i] += dx;
y[ik] = y[i] - dy; y[i] += dy;
}
h += d;
}
}
if (! inverse) /* 逆変換でないならnで割る */
for (i = 0; i < n; i++) { x[i] /= n; y[i] /= n; }
return 0; /* 正常終了 */
}
#define N 64
//FFT関連(1)終了↑
//インクルド(SSD1306用)
//#include <Arduino.h>
//#include <Wire.h>
//定義(SSD1306)
#define MAX_PAGE (7)
#define MAX_COL (127)
#define COMMAND_MODE 0x80 // continuation bit is set!
#define DATA_MODE 0x40
#define SET_COLUMN_ADDRESS 0x21 // takes two bytes, start address and end address of display data RAM
#define SET_PAGE_ADDRESS 0x22 // takes two bytes, start address and end address of display data RAM
#define SET_MEMORY_ADDRESSING_MODE 0x20 // takes one byte as given above
#define HORIZONTAL_ADDRESSING_MODE 0x00
#define SET_SEGMENT_REMAP_0 0xA0 // column address 0 is mapped to SEG0 (Reset)
#define SET_SEGMENT_REMAP_127 0xA1 // column address 127 is mapped to SEG0
#define SET_COMMON_REMAP_0 0xC0 // row address 0 is mapped to COM0 (Reset)
#define SET_COMMON_REMAP_63 0xC8 // row address 63 is mapped to COM0
//I2Cに配列を転送する
void write_s(uint8_t *str1, uint8_t len1) {
Wire.beginTransmission( 0x3c );
for (int ii = 0; ii < len1; ii++) {
//一文字出力
Wire.write(*str1 ++);
}//for
Wire.endTransmission();
}//write_s
//セットページアドレス
void setPageAddress(uint8_t start, uint8_t end)
{
uint8_t databytes[6] = {COMMAND_MODE, SET_PAGE_ADDRESS, COMMAND_MODE, start, COMMAND_MODE, end};
write_s(databytes, 6);
}//setPageAddress
//セットカラムアクセス
void setColumnAddress(uint8_t start, uint8_t end)
{
uint8_t databytes[6] = {COMMAND_MODE, SET_COLUMN_ADDRESS, COMMAND_MODE, start, COMMAND_MODE, end};
write_s(databytes, 6);
}//setColumnAddress
//セットメモリーアドレシングモード
void setMemoryAddressingMode()
{
uint8_t databytes[4] = {COMMAND_MODE, SET_MEMORY_ADDRESSING_MODE, COMMAND_MODE, HORIZONTAL_ADDRESSING_MODE};
write_s(databytes, 4);
}//setMemoryAddressingMode
//セットディスプレー Flip
void setDisplayFlip(int left, int down)
{
if ( left == 0) {
// column address 0 is mapped to SEG0 (Reset)
//_sendCommand(SET_SEGMENT_REMAP_0);
uint8_t databytes[2] = {COMMAND_MODE, SET_SEGMENT_REMAP_0};
write_s(databytes, 2);
} else {
// column address 127 is mapped to SEG0
//_sendCommand(SET_SEGMENT_REMAP_127);
uint8_t databytes[2] = {COMMAND_MODE, SET_SEGMENT_REMAP_127};
write_s(databytes, 2);
}//end if
if ( down == 0) {
// Reset mode
//_sendCommand(SET_COMMON_REMAP_0);
uint8_t databytes[2] = {COMMAND_MODE, SET_COMMON_REMAP_0};
write_s(databytes, 2);
} else {
// Flip Up/Down (Need to rewrite display before H effect shows)
//_sendCommand(SET_COMMON_REMAP_63);
uint8_t databytes[2] = {COMMAND_MODE, SET_COMMON_REMAP_63};
write_s(databytes, 2);
}//end if
}//setDisplayFlip
//8バイト分まとめて出力
void put8byte(int qq) {
//データの配列の定義
static uint8_t databytes[9] = {DATA_MODE, 0, 0, 0, 0, 0, 0, 0, 0};
static int byte_count = 1;
databytes[byte_count] = qq;
byte_count++;
if ( byte_count > 8 ) { //8バイト分、貯まったら
write_s(databytes, 9);
byte_count = 1;
} // endif
} //put8byte
//ドットを打つ
void Dot(int x, int y, int c) {
static int b_count = 0; //ビットカウント
static int qq = 0; //一時
qq = qq | ( c << b_count);
b_count++;
if ( b_count > 7 ) { //1バイト分、貯まったら
put8byte(qq); //SSD1306に出力
qq = 0;
b_count = 0;
}//end if
}//DOt
//キャラクターRAMの内容
int y_[] = {
//1 2 3 4 5 6 7 8
1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , //1
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //2
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //3
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //4
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //5
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //6
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //7
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //8
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //9
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //A
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //B
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //C
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //D
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //E
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //F
56 , 57 , 58 ,59 ,60 ,61 ,62 ,63 //G
};
//再表示
void display(void) {
int y; int x;
//範囲の設定 (OLED内部のx,yカウンターを初期化してホームポジション0,0に)
setPageAddress(0, MAX_PAGE); // all pages
setColumnAddress(0, MAX_COL); // all columns
for (int ii = 0; ii < 8192; ii++) {
//SSD1306のバッファーの配置順のxとyを求める
y = ((ii & 0b0001110000000000 ) >> 7) + ( ii & 0b0111);
x = (ii & 0b0000001111111000) >> 3;
//↓開始 グラフのメイン処理 x=0...127 , y=0...64
if (y < (y_[x]+1)) {
Dot(x, y, 1); //白のドットを打つ
} else {
Dot(x, y, 0); //黒のドットを打つ
}//end if
//↑終了
}//for ii
}//display
//SSD1306の初期化
void display_begin(void) {
//debug
// initialize serial communication at 9600 bits per second:
//Serial.begin(9600);
//Serial.println();
//Serial.println("START");
//Serial.println();
//I2Cの初期化
Wire.begin(); //H743
delay(200);
//Wire.setClock(2000000); //速度の変更 (I2C高速化 2Mhz)
//Wire.setClock(1000000); //速度の変更 (I2C高速化 1Mhz)
Wire.setClock(800000); //速度の変更 (I2C高速化 800khz)
//Wire.setClock(400000); //速度の変更 (I2C高速化 400khz)
//Wire.setClock(200000); //速度の変更 (I2C高速化 200khz)
//Wire.setClock(100000); //速度の変更 (I2C高速化 100khz)
delay(200);
//SSD1306の初期化スペル(魔法)
//0x80,0x8D,0x80,0x14,0x80,0xAF
write_s( (uint8_t*) "\200\215\200\024\200\257", 6);
delay(100);
//セットメモリーアドレシングモード (画面の終端に来たら画面の先頭に)
setMemoryAddressingMode();
//セットディスプレー Flip (画面の向きを変える)
setDisplayFlip(0, 1);
}//display_begin
//初期化
void setup() {
//SSD1306の初期化
display_begin();
}//setup
//メインループ
void loop() {
//FFT関連(2) 開始↓
int i; //ループカウント
//FFT配列
static double x1[N], y1[N], x2[N], y2[N], x3[N], y3[N];
//ランダムウオーク debug
static int L1 = 0;
//入力
//int summ = 0;
for (i = 0; i < N; i++) {
//ランダムウオーク debug
L1 = L1 + ( random(16) - 8 );
if ( L1 < 0 ) {L1 = 0;} else if ( L1 > 255 ) {L1 = 255;}
//L1 = 128;
//microseconds = micros();
//x1[i] = ((analogRead(A3)) >> 2) - 128; // Fitting analogRead data (range:0 - 1023) to int8_t array (range:-128 - 127)
//x1[i] = gg[i] - 128; //規則性のあるデータ debug
//x1[i] = x2[i] = 6 * cos( 6 * PI * i / N);
x1[i] = x2[i] = 6*((double)(L1 - 128))/100.0; //ランダムウオーク debug
y1[i] = y2[i] = 0;
//summ += x1[i];
//while (micros() < (microseconds + sampling_period_us)) { // Timing out uC ADC to fulfill sampling frequency requirement
//}
}//for
//FFT変換
//for (i = 0; i < N; i++) {
// x1[i] = x2[i] = 6 * cos( 6 * PI * i / N)
// + 4 * sin(18 * PI * i / N);
// y1[i] = y2[i] = 0;
//}
if (fft(N, x2, y2)){
//return 1;
return;
}
//逆変換 debug
for (i = 0; i < N; i++) {
x3[i] = x2[i]; y3[i] = y2[i];
}
if (fft(-N, x3, y3)){
//return 1;
return;
}
//debug シリアル出力
//Serial.print(" 元のデータ フーリエ変換 逆変換\n");
//for (i = 0; i < N; i++){
// Serial.print(i);
// Serial.print("|");
// Serial.print(x1[i]); Serial.print(' ');
// Serial.print(y1[i]);
// Serial.print("|");
// Serial.print(x2[i]); Serial.print(' ');
// Serial.print(y2[i]);
// Serial.print("|");
// Serial.print(x3[i]); Serial.print(' ');
// Serial.print(y3[i]);
// Serial.println();
//}//for debug
//FFT関連(2) 終了↑
//SSD1306への出力 開始 ↓
int t,a;
for(i=0;i < (N/2);i++){
a = x2[i]*100; //グラフへ値のセット
t = i << 2; //x4
y_[t++]=a;
y_[t++]=a;
y_[t++]=-1;
y_[t++]=-1;
}//for ii
//y_[0]=32; //debug
display(); //再表示
delay(200); //0.2秒待つ debug
//SSD1306への出力 終了 ↑
}//loop