方形波のフーリエ級数による合成プログラム。 http://www.vector.co.jp/authors/VA000874/のリンク先http://homepage3.nifty.com/silo/FONTV/でダウンロードした VGON16.LZH のうち GONHN16X.TLF をQSPIフラッシュROMの0x90000000番地以降に、GONZN16X.TLF を0x90010000番地以降に配置してから実行して日本語表示をさせることが可能。

Dependencies:   BSP_DISCO_F746NG_patch_fixed BUTTON_GROUP LCD_DISCO_F746NG QSPI_DISCO_F746NG TS_DISCO_F746NG mbed

main.cpp

Committer:
kawaberi12891
Date:
2016-02-02
Revision:
1:da55c6735fa0
Parent:
0:529a855826ec

File content as of revision 1:da55c6735fa0:

/*
 F746_Fourier_series_of_square_wave_01
 フーリエ級数の部分和に基づき正弦波を合成して方形波を生成するプログラム

 書籍「フーリエ変換とラプラス変換 三上直樹著 工学社
 ISBN978-4-7775-1802-9
 P.14 デモプログラムを参考にして作成しました。

 実行上の注意
 日本語フォントを使用しています。
 http://www.vector.co.jp/authors/VA000874/のリンク先
 http://homepage3.nifty.com/silo/FONTV/でダウンロードした
 VGON16.LZH のうち GONHN16X.TLF をQSPIフラッシュROMの0x90000000番地以降に、
 GONZN16X.TLF を0x90010000番地以降に配置してから実行して日本語表示をさせることが可能です。
*/

#include "mbed.h"
#include <stdlib.h>
#include <string.h>
#include "LCD_DISCO_F746NG.h"
#include "TS_DISCO_F746NG.h"
#include "QSPI_DISCO_F746NG.h"
#include "button_group.hpp"
#include "sjis_utf_table.h"

using namespace Mikami;

LCD_DISCO_F746NG lcd;           // Object for LCD display
TS_DISCO_F746NG ts;             // Object for touch pannel
QSPI_DISCO_F746NG qspi;
DigitalIn user_button(USER_BUTTON);
DigitalOut led1(LED1);
DigitalOut led2(LED2);

#define BUFFER_SIZE ((uint32_t)32)
#define NB          0x10011; // 全角フォントのコード・ブロック数 NBがあるオフセットアドレス 0x11
#define countof(x)  ( sizeof(x) / sizeof(x[0]) )
#define LEN         60

const float PI = 3.141593f;
const int N_ = 480;
const double DT_ = ((2.2*PI)/(N_ - 1));
const float DX_ = 0.3f;
  
const uint32_t BACK_COLOR          = 0xFF006A6C;    // Teal green
const uint32_t INACTIVE_COLOR      = BACK_COLOR & 0xD0FFFFFF;
const uint32_t TOUCHED_COLOR       = 0xFF7F7FFF;
const uint32_t ORIGINAL_COLOR      = 0xFF0068B7;
const uint32_t INACTIVE_TEXT_COLOR = LCD_COLOR_GRAY;
const uint32_t AXIS_COLOR          = 0xFFCCFFFF;
const uint32_t LINE_COLOR          = LCD_COLOR_CYAN;

int order_ = 1; // フーリエ合成の次数

int x_size, y_size; //液晶画面の横・縦のドット数
const float PI2 = PI*2;
const int FS_ = 48000;
const int MAX_ORDER_ = 199; // 使用する係数の最大の次数
double bk1_[MAX_ORDER_];    // フーリエ係数       

uint32_t hankaku_base_addr; // 半角フォントの先頭アドレス+0x90000000
uint32_t zenkaku_base_addr; // 全角フォントの先頭アドレス+0x90010000
uint32_t kanji_start_addr;
uint8_t  WF;                        // 半角フォントのフォント幅 WF
char input_string[LEN];             // 表示文字列を入れる配列
unsigned int output_utf8[LEN][3];   // 新check_utf8()の出力を受け取るための配列

struct point {
    float x;
    float y;
};

struct point ptn1_[N_];

void lcd_init(void);
void qspi_init(void);
void font_init(void);
void check_utf8(char *, unsigned int [256][3]);
void drawfont16(uint16_t, uint16_t, char, uint32_t); // draw  8x16 font
void draw_zenkaku(uint16_t, uint16_t, uint32_t, uint32_t); // draw 16x16 font
const char *findface(unsigned int);
void draw_string(uint16_t, uint16_t, char *, uint32_t);
char *itoa(int, char *, int);

void Synthesize(void);          // フーリエ合成関数
void CreateCoefficients(void);  // フーリエ係数生成関数
        
int main()
{
    int i, j;

    printf("Hello Discovery F746NG!\r\n");
    printf("SystemCoreClock: %d MHz\r\n\n", SystemCoreClock/(1000*1000));
    CreateCoefficients();       // フーリエ係数の生成   
    qspi_init();
    lcd_init();
    font_init();  
    draw_string(0, 0, "方形波のフーリエ級数による合成", LCD_COLOR_WHITE);  
    draw_string(0, 250, "フーリエ級数の次数 N =", LCD_COLOR_WHITE);       
    // Setting of button group 
    const string DOWN_UP[2] = {"DOWN", "UP"}; // downUP は フーリエ級数の時数を増減させるためのボタングループ
    ButtonGroup downUp(lcd, ts, 350, 230, 60, 40, ORIGINAL_COLOR, BACK_COLOR, 2, DOWN_UP, 5, 0, 2);
    downUp.Draw(0, INACTIVE_COLOR, INACTIVE_TEXT_COLOR);
    downUp.Draw(1, TOUCHED_COLOR, INACTIVE_TEXT_COLOR);

    while (1) {
        Synthesize();         // フーリエ係数で波形を合成                      
        for (i = 0; i < x_size-20; i++)
        {
            lcd.DrawPixel(i, ptn1_[i].y + 100, LCD_COLOR_WHITE);
        }
        downUp.Draw(0, TOUCHED_COLOR, INACTIVE_TEXT_COLOR);        
        downUp.Draw(1, TOUCHED_COLOR, INACTIVE_TEXT_COLOR);
        while (!downUp.Touched(0, TOUCHED_COLOR) && !downUp.Touched(1, TOUCHED_COLOR)) {} // ボタンが押されるのを待つ
        if (downUp.Touched(1, TOUCHED_COLOR)) {
            printf("UP Touched\n");
            if (order_ < 199) {
                order_++;
            }
            lcd.Clear(BACK_COLOR);
        }else if (downUp.Touched(0, TOUCHED_COLOR)) {
            printf("DOWN Touched\n");
            if (order_ > 1) {
                order_--;
            }
            lcd.Clear(BACK_COLOR);
        }
        itoa(order_, input_string, 10);
        draw_string(182, 250, input_string, LCD_COLOR_WHITE);
        draw_string(0, 0, "方形波のフーリエ級数による合成", LCD_COLOR_WHITE);  
        draw_string(0, 250, "フーリエ級数の次数 N =", LCD_COLOR_WHITE);
    }
}


void lcd_init(void)
{    
    lcd.Clear(BACK_COLOR);
    lcd.SetTextColor(LCD_COLOR_YELLOW);
    lcd.SetFont(&Font20);     
    x_size = lcd.GetXSize();
    y_size = lcd.GetYSize();
}

void Synthesize(void) // bk1_[]を使用するハミング窓「なし」版
{
    const float A0 = -50;
    const double SHIFT = -0.1 * PI;
    float xn[N_];
    int n, k;

//    printf("DT_ = %f\n", DT_);  
    for (n = 0; n < N_; n++) {
        xn[n] = 0;
        for (k = 1; k <= order_; k++) {
            xn[n] = xn[n] + (float)(bk1_[k-1]*sin(k*(DT_*n + SHIFT)));
        }
//        printf("%f\n", n, xn[n]);
    }
    for (n = 0; n < N_; n++) {
        ptn1_[n].x = DX_ * n;
        ptn1_[n].y = A0 * xn[n]; 
//        printf("ptn1_[%d].x=%f : ptn1_[%d].y=%f\n", n, ptn1_[n].x, n, ptn1_[n].y);
    }
}

// フーリエ係数の生成
void CreateCoefficients(void)
{
    int k;
    const float FACTOR = 4.0/PI;  // 4/π

    for (k=1; k<=MAX_ORDER_; k++)
    {
        if (k % 2 == 0)
        {
            bk1_[k-1] = 0;
        }else{
            bk1_[k-1] = FACTOR/(double)k;
        }
    }
}

// draw 8x16 font
void drawfont16(uint16_t Xpos, uint16_t Ypos, char c, uint32_t color)
{
    int i, j;
    const short data[] = {
        0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01,
    };
    uint8_t ReadBuffer[BUFFER_SIZE];
    uint32_t addr;
    addr = c * 0x10 + hankaku_base_addr;
    qspi.Read(ReadBuffer, (uint32_t)addr, BUFFER_SIZE);
    i = j = 0;
    for (j = 0; j < 16; j++) { // 半角1文字分16Byteを読み出す
        for (i = 0; i < 8; i++) { // メモリ1Byte分の8
            if (ReadBuffer[j] & data[i]) {
                lcd.DrawPixel((Xpos + i), (Ypos + j), color);
            }
        }
    }
}


// 文字列表示関数
void draw_string(uint16_t Xpos, uint16_t Ypos, char *str, uint32_t color)
{
    int i = 0, j;
    unsigned int utf8_code;
    
    check_utf8(str, output_utf8);
    while (output_utf8[i][2] != 0) {
        utf8_code = 0;
        if (output_utf8[i][0] == 0) { // 1バイト文字の場合
            drawfont16(Xpos, Ypos, output_utf8[i][2], color);
            Xpos += 8;
        } else { // 2バイト文字(UTF-8)の場合
            utf8_code = output_utf8[i][0] << 16;
            utf8_code += output_utf8[i][1] << 8;
            utf8_code += output_utf8[i][2];
            draw_zenkaku(Xpos, Ypos, utf8_code, color);
            Xpos += 16;
        }
        i++;
    }
    for (i = 0; i < LEN; i++) {
        output_utf8[i][0] = 0;
    }
}

// findface() UTF-8コードを引数で受け取り、SJISに変換してフォントデータを検索し、先頭アドレスを返す
const char *findface(unsigned int utf8_code)
{
    const char *p = NULL;
    unsigned int sjis_code;
    int i;
    uint32_t addr;

    for (i = 0; i < countof(utf8_sjis_table); i++) {
        if (utf8_code == utf8_sjis_table[i].utf8) {
            sjis_code = utf8_sjis_table[i].sjis;
        }
    } 
    // Shift-JISコードをもとにフォントテーブルデータを参照しフォントインデックス番号を得る
    if (sjis_code < 0x879f) {
        i = 0;
        while (sjis_code != utf8_sjis_table[i].sjis) {
            i++;
        }
        addr = zenkaku_base_addr + (utf8_sjis_table[i].line -1) * 0x10;
    }else{ // SJISコード 0x889F 亜 以降の文字コードを持つ文字
        i = 523;
        while (sjis_code != utf8_sjis_table[i].sjis) {
            i++;
        }
        addr = kanji_start_addr + ((utf8_sjis_table[i].line - 2445) << 4);
    }     
    p = (const char *)addr;        
    return p;
}


// draw 16x16 font
void draw_zenkaku(uint16_t Xpos, uint16_t Ypos, unsigned int utf8_code, uint32_t color)
{
    int i, j, k;
    const short data[] = {
        0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01,
    };
    const char *p;
    uint8_t ReadBuffer[BUFFER_SIZE];

    p = findface(utf8_code);
    if(p == NULL) return;   
    
    if (qspi.Read(ReadBuffer, (uint32_t)p, BUFFER_SIZE)!= QSPI_OK) {
        error("Read FAILED\n");
    } else {
        ;
    }
    k = 0;
    for (i = 0; i < 32; i++) { // 漢字1文字分32Byteを読み出す
        if (k > 15) { // k=0,1,2,,,15 は漢字データの横1列16bit
            k = 0;
        }
        for (j = 0; j < 8; j++) { // メモリ1Byte分の8
            if (ReadBuffer[i] & data[j]) {
                lcd.DrawPixel((Xpos + k), (Ypos + i/2), color);
            }
            k++;
        }
    }
}


// 文字列を読み込み、1バイト半角英数文字と3バイトUTF-8文字の判別して
// 結果を2次元配列に返す関数
// ※2バイトUTF-8文字は無視するので注意
void check_utf8(char *sp, unsigned int utf8[256][3])
{
    int i, j; // 文字数を数えながら、配列の添え字をインクリメントするための変数
    char c1 = 0, c2 = 0;
    unsigned int i1, i2, i3;

    i = 0;
    while (*sp != '\0') {
        j = 0;
        if ((*sp & 0xf0) == 0xe0) { // 上位4ビットが1110なら、3バイト文字の1バイト目
            c1 = *sp;
            i1 = c1;
            i1 *= 0x10000;
        } else if ((*sp & 0xc0) == 0x80) { // 上位2ビットが10なら、他バイト文字の2バイト目以降
            if (c2 == 0) {
                c2 = *sp;
                i2 = c2;
                i2 *= 0x100;
            } else {
                utf8[i][j] = c1;
                j++;
                utf8[i][j] = c2;
                j++;
                utf8[i][j] = *sp;
                i3 = *sp;
                i3 = i1 + i2 + i3;
                c1 = c2 = 0;
                i++; // 次の文字へ進む
            }
        } else if ((*sp & 0x80) == 0) { // 1バイト文字の場合
            utf8[i][2] = *sp;
            i++; // 次の文字へ進む
        }
        sp++;
    }
    for (j = 0; j < 3; j++) {
        utf8[i][j] = 0; // データの終りが必ず0になるように書き込む
    }
}

char *itoa( int val, char *a, int radix )
{
    char *p = a;
    unsigned int v = val;/* 作業用(変換対象の値) */
    int n = 1;/* 変換文字列の桁数記憶用 */
    while(v >= radix) { /* 桁数を求める */
        v /= radix;
        n++;
    }
    p = a + n; /* 最下位の位置から設定する */
    v = val;
    *p = '\0';/* 文字列終端の設定 */
    do {
        --p;
        *p = v % radix + '0';/* 1桁の数値を文字に変換 */
        if(*p > '9') {/* 変換した文字が10進で表現できない場合 */
            *p = v % radix - 10 + 'A'; /* アルファベットを使う */
        }
        v /= radix;
    } while ( p != a);
    return a;
}


void qspi_init(void)
{
    QSPI_Info pQSPI_Info;
    // Check initialization
    if (qspi.Init() != QSPI_OK) {
        led2 = 1;
        error("Initialization FAILED\n");
    } else {
        led1 = 1;
    }
    // Check memory informations
    qspi.GetInfo(&pQSPI_Info);
    if ((pQSPI_Info.FlashSize          != N25Q128A_FLASH_SIZE) ||
            (pQSPI_Info.EraseSectorSize    != N25Q128A_SUBSECTOR_SIZE) ||
            (pQSPI_Info.ProgPageSize       != N25Q128A_PAGE_SIZE) ||
            (pQSPI_Info.EraseSectorsNumber != N25Q128A_SUBSECTOR_SIZE) ||
            (pQSPI_Info.ProgPagesNumber    != N25Q128A_SECTOR_SIZE)) {
        led2 = 1;
        error("Get informations FAILED\n");
    } else {
        ;        
    }

}



void font_init(void)
{
    uint8_t ReadBuffer[BUFFER_SIZE]; 
    uint32_t read_write_addr;
    
    // 半角フォント処理
    // フォントイメージ開始アドレスを求める
    read_write_addr = 0x0d; // フォント幅(WF)を読み取る
    if (qspi.Read(ReadBuffer, read_write_addr, 0x1) != QSPI_OK) {
        led2 = 1;
        error("Read FAILED\n");
    } else {
        WF = ReadBuffer[0];
        hankaku_base_addr = 0x11;
    } // 0x11はフォント先頭からフォントイメージまでのオフセット
    
    // 全角フォント処理
    // コード・ブロック数 NB を読み出し、フォントイメージ開始アドレスを求める
    read_write_addr = NB;
    if (qspi.Read(ReadBuffer, read_write_addr, 0x1) != QSPI_OK) {
        led2 = 1;
        error("Read FAILED\n");
    } else {
        zenkaku_base_addr = 0x10000 + 0x12 + ReadBuffer[0] * 4;
    } // 0x10000は全角フォントを0x90010000から配置してあるため 0x12はフォント先頭からNBまでのオフセット
    kanji_start_addr = zenkaku_base_addr + 39104; // 39104はフォント先頭から漢字先頭"亜"までのオフセット  
}