スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

WAVプレーヤーの製作(その12 小型TFT LCD, part2)

先回、小型TFT LCDについて述べたが、まだ諦めきれずに弄っています。

課題は
1.このアイコン背景色の問題
2.タッチパネルが4線式のA/D入力なのでSPIに慣れた身には未体験領域だ。安定度が心配です。
  ソフトも新作を要します。
3.タッチの割り込みはどうやるのだろうか? A/Dの割り込みってあるの? 動作安定するの?

1.は一応の暫定処置ができたので、今回は2.と3.です。

【タッチができるようになりました】
stm32_proj_47.jpg

左右・上下計測を順に行うために、DC電圧出力とA/D計測を順に切り替えながら計測しなければならないので、これは大変かなと覚悟していたのですが、結構あっさりとできました。

ボールペンでスクリーンの真ん中へんを押してます。xは上下、yは左右方向です。
左上にタッチされた座標を表示させていますが、下一桁が0なので、ぴったり安定のように見えてしまいますが、実際の表示はかなり数値がバラツイテいます。

これだけならば、良さそうに思ってしまいますが、実はx、y方向の数値分布がゆがんでます。

stm32_proj_48.jpg

この画像はスクリーン9箇所をタッチして、おおよその値を読んだものです。上の画像とは異なり (x左右,y上下)で表記してます。
スクリーンの中央付近の上下左右電圧を読んで、(0-320,0-240)になるようにスケーリングしてあります。左下がゼロです。

ところが、左右・上下がかなり歪の大きい値になります。特に、左側の上下方向は酷い状態で使い物になりません。

この4線抵抗膜式タッチスクリーンのAD値はタッチしないとほぼゼロで、タッチするとある値以上にピッと上がるので、「アナログウォッチドック」という割り込み機能を使えば(まだ調べていないが)、音楽演奏中に停止時間僅かでボリューム変更ができるのだが、タッチ精度が悪いと操作ができないとなってしまう。

まあ、この小さなTFT液晶は、表示専用か、タッチ機能を使うとしても大雑把な機能に減退されるのかもしれません。


折角、タッチができるようになったので、その処理内容を備忘録とします。
-----------------
私はこれまでpicマイコンを使ってきたので、後閑さんのタッチスクリーンの記事を参考にさせて頂きました。
ここにはタッチスクリーンの原理が、とっても解りやすく図解されているので、stm32のソフトに反映するのがとても楽にできました。

・stm32側で用意するハード
 1) アナログ入力とGPIO入力に切り替えできるピンを2個、これを LCDタッチの y-,x- 端子に配線する
 2) GPIO出力と入力が切り替えられるピンを2個、 これを LCDの y+,x+端子に配線する
(picと違い、stm32には訳も無いことですね)
 3) ノイズ防止のため、この4ピンは10kぐらいの抵抗でプルダウンしておく。
 4) A/D入力に使うピン2個には、これもノイズ防止のため0.1uF程度のコンデンサをGND間に付ける。

・ソフト
動作と計測の手順は
 計測してもらう側:座標数値を大きくしたい側に+3V、反対側を0(GND)にする
 計測する側   :一方をアナログ計測、もう一方を入力(フローティング)とする → 電圧を計測する

 以上の動作をするチャンネルを切り替えて別方向の座標を計測する。

【GPIOを切り替えてx,yを交互に計測する部分】
この後で、計測した10回の rPoint データの真ん中辺3点の平均値を使ってます。

void readTouch(void)
{
  uint16_t rPoint[2][10],temp = 0;
  uint8_t t,t1,count;

  // OUT DC GPIOB_Pin7(Y+), IN DC GPIOA_Pin3(Y-)
  GPIO_Conf_touchY();     // y 方向への切り替え

  GPIO_SetBits(GPIOB,GPIO_Pin_7);   //PB7(Y+) is OUT DC
  GPIO_ResetBits(GPIOA,GPIO_Pin_3); //PA3(Y-) is IN DC
  GPIO_ResetBits(GPIOB,GPIO_Pin_6); //PB6(X+) is IN DC
                                      //PA2(X-) is Analog IN
  delay_ms(5);  // 安定時間
  for(count = 0; count<10; count++ )  //10回計測する
    {
      Present_ParamY = ReadADC1(ADC123_IN2_CH);   //STBee.hへDefine追加してます
      delay_ms(1);
      if(Present_ParamY > 3000 || Present_ParamY < 5000){
          rPoint[0][count] = Present_ParamY;
      }
    }

  // OUT DC GPIOB_Pin6(X+), IN DC GPIOA_Pin2(X-)
  GPIO_Conf_touchX();   // x 方向に切り替える

  GPIO_SetBits(GPIOB,GPIO_Pin_6);   //PB6(X+) is OUT DC
  GPIO_ResetBits(GPIOA,GPIO_Pin_2); //PA2(X-) is IN DC
  GPIO_ResetBits(GPIOB,GPIO_Pin_7); //PB7(Y+) is IN DC
                                      //PA3(Y-) is Analog IN
  delay_ms(5);  // 安定時間
  for(count = 0; count<10; count++ )
    {
      Present_ParamX = ReadADC1(ADC123_IN3_CH);  //STBee.hへ追加
      delay_ms(1);
      if(Present_ParamX > 3000 || Present_ParamX < 5000){
          rPoint[1][count] = Present_ParamX;
      }
    }


【y方向へ切り替え】
void GPIO_Conf_touchY(void)
{
  //GPIOD,GPIOE clock set
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOA, ENABLE);

  GPIO_InitTypeDef GPIO_initstruc;

  // OUT DC (Y+) pin = PB7
  GPIO_initstruc.GPIO_Pin=GPIO_Pin_7;
  GPIO_initstruc.GPIO_Mode=GPIO_Mode_Out_PP;
  GPIO_initstruc.GPIO_Speed=GPIO_Speed_50MHz;
  GPIO_Init(GPIOB,&GPIO_initstruc);

  // IN DC (Y-) pin = PA3
  GPIO_initstruc.GPIO_Pin=GPIO_Pin_3;
  GPIO_initstruc.GPIO_Mode=GPIO_Mode_IN_FLOATING;
  GPIO_initstruc.GPIO_Speed=GPIO_Speed_50MHz;
  GPIO_Init(GPIOA,&GPIO_initstruc);

  // IN DC (X+) pin = PB6
  GPIO_initstruc.GPIO_Pin=GPIO_Pin_6;
  GPIO_initstruc.GPIO_Mode=GPIO_Mode_IN_FLOATING;
  GPIO_initstruc.GPIO_Speed=GPIO_Speed_50MHz;
  GPIO_Init(GPIOB,&GPIO_initstruc);
  ADC_InitTypeDef  ADC_InitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;

  /* ADCCLK = PCLK2/6 = 72/6 = 12MHz*/
  RCC_ADCCLKConfig(RCC_PCLK2_Div6);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | ADC_IN0_3_GPIO_RCC, ENABLE);

  GPIO_InitStructure.GPIO_Pin = ADC123_IN2_PIN;  // PA2(X-) pin is Analog IN
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  GPIO_Init(ADC123_IN2_PORT, &GPIO_InitStructure);
}

【x方向に切り替え】
void GPIO_Conf_touchX(void)
{
  //GPIOD,GPIOE clock set
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOA, ENABLE);

  GPIO_InitTypeDef GPIO_initstruc;

  // OUT DC (X+) pin = PB6
  GPIO_initstruc.GPIO_Pin=GPIO_Pin_6;
  GPIO_initstruc.GPIO_Mode=GPIO_Mode_Out_PP;
  GPIO_initstruc.GPIO_Speed=GPIO_Speed_50MHz;
  GPIO_Init(GPIOB,&GPIO_initstruc);

  // IN DC (X-) pin = PA2
  GPIO_initstruc.GPIO_Pin=GPIO_Pin_2;
  GPIO_initstruc.GPIO_Mode=GPIO_Mode_IN_FLOATING;
  GPIO_initstruc.GPIO_Speed=GPIO_Speed_50MHz;
  GPIO_Init(GPIOA,&GPIO_initstruc);

  // IN DC (Y+) pin = PB7
  GPIO_initstruc.GPIO_Pin=GPIO_Pin_7;
  GPIO_initstruc.GPIO_Mode=GPIO_Mode_IN_FLOATING;
  GPIO_initstruc.GPIO_Speed=GPIO_Speed_50MHz;
  GPIO_Init(GPIOB,&GPIO_initstruc);

  ADC_InitTypeDef  ADC_InitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;

  /* ADCCLK = PCLK2/6 = 72/6 = 12MHz*/
  RCC_ADCCLKConfig(RCC_PCLK2_Div6);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | ADC_IN0_3_GPIO_RCC, ENABLE);

  GPIO_InitStructure.GPIO_Pin = ADC123_IN3_PIN;  // PA3(Y-) pin is Analog IN
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  GPIO_Init(ADC123_IN3_PORT, &GPIO_InitStructure);
}

この他に、ADチャンネルの設定、読み込みがありますが、一般的な設定なので省略します。



・・・小型化は諦めてプレーヤーを完成させなくてはなりません。




スポンサーサイト

WAVプレーヤーの製作(その11 小型TFT LCDに嵌る)

小型コンパクト機器を作るのが身上なので、今使っている3.2インチよりもずっと小さい2.4インチの物が目に止まった。50%off祭りとかで、何と!625円です。

TFT液晶の動作も勉強になるだろうと思い、ポチッとしたのが運のつきでした。

【やっと動いた2.4インチTFT液晶・S95417】 oh! 撮影のデジカメが反射で写ってます。

stm32_proj_39.jpg

上が3.2インチ液晶です。長さ比は75%なのですが、面積比は約半分なので、やはり半分に見えます。
S95417がこの液晶の商品型番ですが、コントローラチップはst7787です。
これを使うに当たり、いちばん難しいところは初期化コードなのですが、ねむいさんのところのものをお借りするつもりでおりました。

・・・・・・・ところが、ところがですよ、うんともすんとも動きません。フルに4日間を費やしましたヨ。
あれやこれやと試しましたが、功無く挫折か?

気を取り直し、ハード故障かなと思いを転じ、複数個(激安なので)買っておいたものに交換しようとして、ハット気がつきました。この1個だけのシルク印刷が違うのです!  

・・・・・・交換したら、何のことなく動くではありませんか。
何だこれは。ヘロヘロです。

【誤品だったTFT液晶・cp2401】  4日間の激闘でパネルの保護シートが剥がれかけてます。
stm32_proj_41.jpg

外見がS95417にそっくりなcp2401です。白の印字を良く見ると[2401]と見えます。
一番上の写真がs95417ですが、これはこれで、s95417等の型番は付いてません。0h! 中華品質?
cp2401の初期化コードに換えると、 ハイ、これも見事に動きました。 やれやれでした。


出鼻をくじかれて始まった小型液晶ですが、動くとなるとssd1289コントローラ搭載の3.2インチ液晶の画面を出したくなります。動かない時にNETを徘徊したのですが、s95417(st7787)を動かしたという記事は、ねむいさんところでしか見つけられませんでした。

st7787のドライバー・コードを見ると、グラフィック・データを表示するルーチンはありますが、点、線、文字を表示させるコードはありませんね。こうなれば、ssd1289のコードやデータシートをベースにして、st7787のデータシートを辿っていかねばなりません。

以下、備忘録です。
-----------------------------------------------------------------------------------------
・線や文字を表示させるベースは「点」を打つこと。スタートは、Draw_Pixel()です。
void Draw_Pixel(uint16_t x, uint16_t y, uint16_t color)
{
  Set_Cursor(x, y);  // 1) 液晶のGDRAM位置を指定する
  Write_GDDRAM_Prepare(); // 2) 液晶に出力するコマンドを出す
  Write_Data(color); // 3) カラーデータをGDRAMに書き込む
}
以上の 1)、2)、3) が基本、これは具体的にはどうなっているか

1)
void Set_Cursor(uint16_t x, uint16_t y){  //ssd1289
  Write_Command(0x004E, x);  // 0x4Eというコマンドとxの位置を指定する
  Write_Command(0x004F, y);   // 0x4F、同上 y
}

2)
void Write_GDDRAM_Prepare(void){
  LCD->Register = 0x0022;  // GDRAMへ書き取り・読み取りコマンド0x0022を書き込む
}

3)
void Write_Data(uint16_t data){
  LCD->Data = data;
}

これを st7787に適用します。異なるところは、1) と 2) です。

これらのことはst7787 DataSheet のこのような表にまとめられていました。
stm32_proj_38.jpg
1) ssd1289は簡単にx,yを指定できましたが、st7787には点を指定するコマンドは無く、「範囲指定」する機能を使うことになります。
void st7787_rect(uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end){

        // 後述しますが、x <-> y を入れ替えしています
        Write_Cmd(0x002A);              // x-start & x-end RAM ADDR
        Write_Data( y_start>>8);
        Write_Data( y_start);              // start
        Write_Data(y_end>>8);
        Write_Data(y_end);                // end

        Write_Cmd(0x002B);             //  y-start & end RAM ADDR
        Write_Data( x_start>>8);
        Write_Data( x_start);              // start
        Write_Data( x_end)>>8);
        Write_Data( x_end);                // end
}

2)  Write_Cmd(0x002C);              // Write Data to GRAM

・グラフィック液晶は指定した座標位置から連続して点を打っていく機能がベースになっていますが、これのスタート点とその方向を決める必要があり、初期化コード内で行っています。ssd1289液晶がベースになっていますので、st7787(s95417)液晶も同じにすれば、線や文字、そしてグラフィックなどを表示させる関数がそのまま使えるようになるはずです。

そこでやったことは、ssd1289に0,0点から始まる斜め線を描かせ、「原点位置と、x,y方向」を調べる。

#if USE_SSD1289 == 1
  Clear_Screen(LCD_GBLUE);  //for SSD1289
#else
  st7787_clear(LCD_GBLUE);  //for st7787
#endif

//LCD原点&x,y方向テスト
while(1) {
    //座標テスト
    //(x0,y0)-(x100,y100)ライン
    Draw_Line(0, 0, 100, 100, LCD_BLACK);

    //(x0,y0)-(x239,y319) SSD1289 FULL POINT
    Draw_Line(0, 0, 239, 319, LCD_RED);

    //(x0,y0)-(x239,y200)
    Draw_Line(0, 0, 239, 200, LCD_BLUE);


【ssd1289】
stm32_proj_42.jpg
右上が作図開始点、←x方向、↓y方向

初期化プログラムの設定記述内容は
  /*
   * Entry Mode R11h = 6018h
   *
   * DFM1 = 1, DFM0 = 1 => 65k Color Mode
   * ID0 = 1, AM = 1    => the way of automatic incrementing
   *                       of address counter in RAM
   */

  Write_Command(0x0011, 0x6018);




0x0011 でカラーモードとGRAMのアドレスカウンター方向を設定している。
0x18部分がアドレスカウンターの原点と方向を決めるので、0x18は下図の右上図になる。
(写真は下図に対し、時計方向に90度回っている)


さて、st7787で同じ向きになるような設定をするには、データシートのこのデータを参考にする。



原点は左上(B点)、x方向 を下図の下側に、y方向 を右側にするのがこの設定。即ち、MX=MY=0,MV=1



とすると、初期化プログラムは以下の通り(0x0036部分のみ表記)

  Write_Cmd(0x0036);
  Write_Data(0x0020);   //縦:x=320,y=240, st7787_rect(0,0,x,y) 左スタート

この設定を行うことで、LCDは以下の表示となる。ssd1289と同じです。

【st7787(st95417)】
stm32_proj_43.jpg

・・・と、ここまで記述してきて、やっと解りました。 9.12.2 の表を見ると、「X-Y Exchange」とあります。
先に記述した、void st7787_rect() 関数で、x、yを入れ替えた原因はここにあったのです。

【s95417での表示画面】
stm32_proj_40.jpg

苦労してやっと動いたs95417ですが、アイコン・バックが透過しません。色々と設定を変えてみましたが直りません。背景色を何故 BLUEと認識してしまうのだろうか? それが解りません。

このLCDは小さくて好きなのですが、
1.このアイコン背景色の問題
2.タッチパネルが4線式のA/D入力なのでSPIに慣れた身には未体験領域だ。安定度が心配です。
  ソフトも新作を要します。
3.タッチの割り込みはどうやるのだろうか? A/Dの割り込みってあるの? 動作安定するの?

安い代わりに手がかかりすぎるのが最大の欠点のようです。  どうしようかな?

625円で貴重な勉強をさせて頂きました、ということで THE END かな?

------------------------------------------------------
6月16日追記

アイコンバックカラーNGの原因が判りました。
stm32_proj_46.jpg

原因はバックカラーの取得結果が正常値になっていなかったです。過去形で書くと直ったように思えますが、恒久的にはダメ状態が続いています。
上の画像では修復されたように見えますが、実は自分で塗ったバックカラー0x07FFをアイコン作図のバックカラーデータを強制的に割り当てただけです。

st7787ではバックカラー取得に0x002E コマンドを使いますが、正常ならば 0x07FFが取得できるはずなので、取得データは 0x00FCです。色々と設定を試しましたがうまくできません。

まあ、背景に複雑な画像を配置しないプレーンな1色塗りだったら、アイコンの背景色にそれを使えば一応の解決策にはなるかな。それにしても65k色のLCDなのだから、背景色16ビットが読み取れない訳がないはずなのだが・・・ まだ何かを見落としているのかもしれない。





WAVプレーヤーの製作(その10 タッチパネルの割り込み処理)

stm32マイコンのお勉強も既に10回にもなってしまいました。今回はstm32で割り込み処理を行います。

おっと、半月以上ブログ更新をしていなかったら、ブログ編集画面のアイコン群が一部リニューアルされてます。
特に使う「画像挿入」が「ファイル挿入」に変わってます。機能が変わったのかな?

最初にお勉強対象の姿を

【ファイラー画面】
stm32_proj_33.jpg

前回の黄色のバックから薄い水色にしました。
本当は透過できる文字を使い、綺麗な画像の上にファイル名を載せたいのですが、透過文字を使うことや、
何と言ってもメモリが既に440kを超えているのでSTBee環境(512k)ではチョット無理そうです。

【音楽再生画面】
stm32_proj_34.jpg

白い文字やアイコン、枠が目立つようにファイラー画面よりも少し濃い目の水色にしましたが、デジカメ映像は色が薄く写ります。

音楽を再生しながらアイコンボタンをタッチするには、ポーリング処理でだけではちょっと難しそうなので、タッチパネルの割り込み機能を使う必要があります。但し、割り込み後にタッチした座標を得ることはできないので、Play中にタッチ座標のポーリング処理を行いながら、割り込み処理ではタッチ座標からアイコンの位置を判別しそのアイコン番号を得るという処理をしています。
これで取得したアイコン番号から、ボリュームやバスブースト処理はPlayルーチン内で処理します。曲の一時停止はPlay内で行うことになりますが、曲の移動などはPlayを呼び出すルーチンに戻ってから、Playを呼び直すという方法が良いかもしれません。

画面の下にvs1011を使った再生部とイヤホンが見えてます。再生しながら割り込み時の音の状態(音量の増減、バスブーストの効き方、音切れ状態等)を確認しています。

音量調整時は瞬間的に曲再生が中断されるので、小さなプッという音が入りますが、レベルが小さいのでさほど気になりません。音量調整量は2dBピッチにしましたが細かすぎるようです。倍か3倍ピッチ程度が良さそうです。しつこくアイコンを押していると、音楽再生が止まって動かなくなることがあります。これは原因調査と対策を考えねばなりません。
バスブーストのやり方は以前、pic24fでプレーヤーを作ってみた時のpicfunの記事を参考にしています。この時はバスブーストを試さなかったのですが、曲目によっては有用な場合もあるようです。

picfunの記事を読んでいて気が付いたのですが、vs1011eには単一周波数の音を出す機能もあるので、1kHz等を出してオーディオ系統の歪率なども計測する価値はあるかなあ、等とも思いました。さて音源そのものの品質はOKなのでしょうかね?


画面に載せている透過アイコンを自前で作ろうと試みましたが挫折してます。

GIMP2で外側を透過にしたpngファイルを作成します。24x24 pixelを拡大した画像です。
stm32_proj_36.jpg

とんすけさんのブログページで透過画像の作り方を学びました。
ここで紹介されている、pngtopnpやppmtobmpは、最終的にこちらのページでダウンロードしました。

これでできたのがこのbmp24ビット画像です。GIMP2に読み込ませて表示しています。
stm32_proj_37.jpg

残るは最終工程のrgb565画像生成です。

ところが、とんすけさん作のコマンド、bmp2lcd_rgb565のcソースをどうやってもコンパイルできません。
標準入出力の*stdinなどを exturn宣言しているところでのエラーを処置するスキルがありません。
仕方なく、cソースを参考にvb6で処理ツールを作ってみました。

あじゃ! 何だこれは
stm32_proj_35.jpg

どっかのビット処理にミスがありそうですが、1日かかっても解決しませんでした(まだしぶとくやるつもりですが・・・)

しょうがないので、先のプレイ画面のアイコンはとんすけさんのソースから借用いたしました。
とんすけさんのアイコンはとても繊細で良くできています。光が上から当たるのでそのグラディーションが綺麗です。そういう意味でも私のアイコンはまだぜんぜんダメレベルですね(笑) とんすけさんはホントにアマチェアなんでしょうか?ネ
まあ、私のレベルが低すぎるということで納得してますが。


さて本題の割り込み処理ですが、これには例によってすっかり嵌まり込みましたヨ。
以下、備忘録です。

-----------------------------------------------------------------------------------------------------------------

・stm32で割り込み処理(今回はGPIOピンからの割り込みに限定します)するための手順は
(私がここで記載する必要は殆ど無いのですが、1.~5.はmiqnnetに詳しく記載されてます

1.GPIOにクロックの供給と初期化を行う
2.割り込み(EXTI)に使用するGPIOの指定を行う
3.割り込みEXTIの初期化を行う (私の理解ですが、EXTIは割り込みをするそのもの)
4.NVICの設定  (私の理解は、NVICは割り込みを管理するもの)
5.割り込みした処理を行うハンドラーを作る

6.割り込みの許可、不許可を制御する

*私が陥ったミスは、「2.~4.を連続して設定しないと割り込みが機能しない」ということでした。

(動作したコード例)

#define  PEN_PORT                       GPIOE
#define  PEN_GPIO_RCC                   RCC_APB2Periph_GPIOE
#define  PEN_PIN                        GPIO_Pin_3

void NVIC_TOUCHConfiguration(void)
{
  GPIO_InitTypeDef   GPIO_InitStructure;
  EXTI_InitTypeDef EXTI_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;

  //T_PEN
  RCC_APB2PeriphClockCmd(PEN_GPIO_RCC | RCC_APB2Periph_AFIO,ENABLE);
  GPIO_InitStructure.GPIO_Pin = PEN_PIN; //PEN_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(PEN_PORT, &GPIO_InitStructure);

  GPIO_EXTILineConfig(PEN_PORTSOURCE, PEN_PINSORCE);

  EXTI_InitStructure.EXTI_Line = EXTI_Line3;
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);

  NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

意外に net に記載が無かった(少なかった?)のが、割り込みを禁止したり許可したりする処理です。
音楽プレーヤーでは再生を行っている時間の隙間を狙って割り込みをする必要があるが、再生のデータをプレーヤーに転送している最中に割り込まれたくないので、禁止・許可の処理が必須です。
私は以下のコードを使いました。

void NVIC_enable_TOUCH(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;

  NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

void NVIC_disable_TOUCH(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;

  NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
  NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;
  NVIC_Init(&NVIC_InitStructure);
}



まだ これからPlay 画面から中断させたり、戻す、進む、また進度表示や早送り等の機能も実装させたいと思います。

今のサイズは、STBee基板、TFT LCD基板、これを繋ぐ基板(SD,DACを積む)の3層重ねなので大分ごっついです。液晶を基板レスのものを使い、CPU+SD+DAC+電源を載せたスペシャル基板を作れば1枚構成で行ける目算があります。
JTAG等の書き込みやIOピン等も用意すればスペシャル改造も可能になれば更にGoodかな?

いっそのこと、CPUは407系を使えば1MEGAメモリなのでいろいろな機能を拡張できる。

想いは広がります。






WAVプレーヤーの製作(その9 短縮ファイルリスト表示と透過画像)

WAVプレーヤーの製作、変わってstm32マイコンの学習 になってしまっているが、引き続き短縮ファイルリスト表示と、画面に趣を与える透過画像の表示が今回のお勉強でした。

【作った画面】
stm32_proj_30.jpg

バックが黄色ですが、最終的には背景画像を作りたいと思ってます。今回の透過画像の勉強はそれの前段かな。
今回のテーマとやったことを記載します。

1.表示用ファイル名(全て2バイト文字で表示が収まらない長いファイル名は短縮する)
  一行目の「01_マディソン郡の恋(Pi….mp3 の…部分が短縮した部分になります。
  いつものことですが、「C言語でも文字列処理」がお勉強課題でした。

2.リストの先頭にある、♪と右上、右下にあるダブル矢印が透過画像です。
  まだ私には透過画像が作れないので、Motion Player Project からお借りしました。
  バイナリー形式のRGB565画像の組み込み方法、表示の方法を重点課題でした。

------------ 以下、備忘録です ------------

1) ファイルリストのように複数の文字列を2次元配列の文字列として扱う場合の処理の方法

  ・mojiretu[ ][ ] として扱う場合は、mijiretu[ファイル数][文字数] として配列を宣言し、文字列として指定
   するときはmojiretu[ファイル位置] で行う。([文字数]部分は[0]として表記しない)
  <例>
      // fnameを初期化
      memset(fname[i],'\0',strlen(fname[i]));

  ・mojiretu[ ][ ] に他の文字列(今回はFatFSから取得したファイル名 fn)からコピーするときは1文字
   ずつコピーする。
   <例>
       for(j = 0; fn[j] != '\0'; j++)       // FatFSから取得した文字列先頭から'\0'以前までを
         {
            fname[num_fname][j] = fn[j];  // 1文字ずつを2次元配列にコピーする
         }
       fname[num_fname][j] = '\0';    // この時点でj は'\0'位置に繰り上がっているので'\0'を入れる

2) 短縮文字の作り方

  ・全角半角が入り混じっていると文字の切れ目を探すのが煩雑のため、全て全角に変換しておく
   (前回のhan2zen関数を使用)
  ・処理コードを見易くするためと、元の文字列を壊さないように、処理をする2次元配列文字列を
   temp[ ] 文字列にコピーしておく
  ・元の文字列から任意の文字数を、挿入する文字列の任意の位置にコピーするにはmemcpy関数を
   使う。

  <例>
        //temp2の半角30文字以降へtempの右半角8文字(全角4文字)を代入
          memcpy(temp2+30, temp+strlen(temp)-8, 8);

3) 透過画像の扱い方

  ・バイナリ画像データ(*.bin)の画像表示方法

   フリー画像ソフト GIMP2 を使う方法を使いました。
   今回使用させて頂いた画像は*.binという拡張子が付いていますが、これを*.data に変更します。
   GIMP2(ver2.8)でインポートします。
   stm32_proj_31.jpg

   画像サイズを指定します。画像の種類をRGB565にします。
   stm32_proj_32.jpg

  ・プログラムコードへの埋め込み方

   この方法は、Motion Player Project に詳細が記述されていますが、このプロジェクトが参照している
   chaNさんの32ビットへの誘いページもとても参考になりました。ここのcソースへの埋め込み を使いました。

   私が行った具体的な方法

   main.c にバイナリーファイルを埋め込むための定義 IMPORT_BIN を記述する。

   /* Import a binary file */
   #define IMPORT_BIN(sect, file, sym) asm (\
           ".section " #sect "\n"             /* Change section */\
           ".balign 4\n"                           /* Word alignment */\
           ".global " #sym "\n"               /* Export the object address to other modules */\
           #sym ":\n"                              /* Define the object label */\
           ".incbin \"" file "\"\n"                /* Import the file */\
           ".global _sizeof_" #sym "\n"      /* Export the object size to oher modules */\
           ".set _sizeof_" #sym ", . - " #sym "\n"    /* Define the object size */\
           ".balign 4\n"                           /* Word alignment */\
           ".section \".text\"\n")               /* Restore section */

   main.c の main()に、binファイルの埋め込みを記述する

    IMPORT_BIN(".rodata", "next_left_32x17.bin", icon_nextLeft); 
     // section, binファイル名, 呼称名
    IMPORT_BIN(".rodata", "next_left_32x17_alpha.bin", icon_nextLeft_alpha);

   main.cの定義エリアに呼称名の宣言を記述する

   extern const char icon_nextLeft[], _sizeof_icon_nextLeft[];
   extern const char icon_nextLeft_alpha[], _sizeof_icon_nextLeft_alpha[];

  ・透過画像表示

   表示をさせるには、以下のコードで行う。

   /*
   int startPosX: 描画開始X座標位置
   int startPosY: 描画開始Y座標位置
   int width: 画像の幅
   int height: 画像の高さ
   const uint16_t *d: 描画する画像へのポインタ
   const uint8_t *a: 描画する画像のアルファチャンネルへのポインタ
   */
   void LCDPutIcon(int startPosX, int startPosY, int width, int height, const uint16_t *d, const uint8_t *a)
   {
     int i, j, x, y = startPosY;
     float alpha_ratio;
     pixel_fmt_typedef pixel_fg, pixel_bg;

    #define F_INV_255 (1.0f / 255.0f) // アルファ率を求める係数

    for(i = 0;i < height;i++){
         x = startPosX;
         for(j = 0;j < width;j++){
             //LCDSetGramAddr(x, y); // ピクセル描画位置指定
             Write_Command(0x004E,y);
             Write_Command(0x004F,320-1-x);
             //LCDPutCmd(0x0022); // 読み書きコマンド発行
             //LCD->RAM; // ダミーリード
             Write_Command(0x0022,LCD->Data);
             pixel_bg.color.d16 = LCD->Data; // バックグラウンドカラーを取得
             pixel_fg.color.d16 = *d++; // フォアグラウンドカラーを取得
             alpha_ratio = *a++ * F_INV_255; // アルファチャンネルからアルファ率を算出

            // フォアグラウンドカラーの各色成分にアルファ率掛ける
             pixel_fg.color.R *= alpha_ratio;
             pixel_fg.color.G *= alpha_ratio;
             pixel_fg.color.B *= alpha_ratio;

            // バックグラウンドカラーの各色成分に(1 - アルファ率)掛ける
             pixel_bg.color.R *= (1.0f - alpha_ratio);
             pixel_bg.color.G *= (1.0f - alpha_ratio);
             pixel_bg.color.B *= (1.0f - alpha_ratio);

            // フォアグラウンドとバックグラウンドを合成
             pixel_fg.color.R += pixel_bg.color.R;
             pixel_fg.color.G += pixel_bg.color.G;
             pixel_fg.color.B += pixel_bg.color.B;

             //LCDSetGramAddr(x, y); // ピクセル描画位置指定
             Write_Command(0x004E,y);
             Write_Command(0x004F,320-1-x);
             //LCDPutCmd(0x0022); // 読み書きコマンド発行
             //LCDPutData(pixel_fg.color.d16); // 合成した色を16bit出力
             Write_Command(0x0022,pixel_fg.color.d16);
             x++;
         }
         y++;
     }
   }
   


さて、次はファイル名を選択して音楽を演奏させることと、その画面作りになります



     

WAVプレーヤーの製作(その8:カラーLCDに漢字表示)

前回、長いファイル名の表示ができたところで、次は漢字やかなを含むファイル名をカラー・グラフィックLCDに表示することが課題です。4月はCMがとても忙しく、やっとこの連休で本格的な取り組みができた。

開発環境は、STBee(stm32F103)+ssd1289 カラーグラフィックLCD+SDIO SDカードを使い、マイコン徹底入門のEclipse IDEを使います。オリジナルのサンプルソフト(小生が一部修正)で、ssd1289のドライブライブラリができており、英数字ならば表示も可能になっているのですが、今回は「日本語表示」がメイン課題です。

グラフィックLCDに文字を表示させるにはビットマップ文字データが必要になる。今回はpicfunの漢字表示グラフィックライブラリを使用させて頂いた。このライブラリでは12×12ドットの漢字データが使われているのでサイズ的に丁度良さそうなのと、オン・メモリーで使いたいので、190kByte程度のオブジェクトサイズという記事を見て使ってみることにしました。

漢字データ(ビットマップ)のドットデータをssd1289の240×320ドットLCDに印字するにはオリジナルのDraw_Pixel関数を使えば良さそうだが、さて表示したい漢字から使用するデータへの橋渡しはどうやってやるのだろうかを調べると、先のpicfunのライブラリの中に漢字コード2Byteから配列インデックスを求める方法が記載されている。これを自分の環境用に変形すれば良さそうです。

・・・・と、簡単に書きましたが、例によって試行錯誤の連続でした。

【ASCII文字の表示】
stm32_proj_22.jpg

picfunで使っているfont.hデータ(5×7ドット)を使ったものですが、小さすぎます。

【漢字データ(0x81,・・・等)で与えた文字の表示】
stm32_proj_29.jpg

ありゃ、横向きでした。このLCDの物理原点は右上なので、行とカラムを入れ替え、行中のカラムデータは物理的最大位置から減ずる方向で作図します。(これは作図ドライバと関連あるかもしれませんが結果合わせでやってます。後日詳細を調べます)

【修正済み】
stm32_proj_23.jpg

stm32_proj_24.jpg

【ファイルリストの表示】
stm32_proj_27.jpg

これぐらいの文字の大きさだったら丁度良いのではないでしょうか?
気付かれたと思いますが、全て2バイト文字になってます。(ここらは改良要かな)

SDIO接続のSDカードから読んでいますが、何故か物理的にPULLアップしないと動かなくなってました。
マイコン徹底入門のEclipse IDE環境ではPULLアップせよ と記載されているので正常な動作なのですが、内部PULLアップ機能を備えた stm32 なのですから、SDIOでのソフトウエア設定は何処でやるのかな? これは宿題です。


完成後の表示を見ると何でもない(簡単なという意味)ことですが、またまた難航しました。以下、備忘録です。

1.二次配列の漢字フォントデータが、KanjiFont12.h というような外部ファイルに置いた状態で
  読み込みができない。

  散々調べましたが、「gccの仕様」らしいとしか解りませんでした。
  →何と! main.c の中に835kbyteものテキストデータを入れ込みました
    更に調べると一次配列として読み込んでから二次にキャストする方法があるらしいですが、未着手です。

2.ASCII文字のフォントが小さすぎる。これを12×12のものに入れ替えると内部メモリを圧迫する
  (現在295k)
ので中止。
  →全てを2バイト文字に変換するルーチンを挿入したこちらのコードをそのまま使用させて頂いた
    半角と全角が混じっていても全てが全角になるのでGoodです。

3.コード上に置いたリテラル文字(漢字)が表示できない。
  →Eclipse IDEのEditorがUTF-8なので、SJISフォント(ANKは1バイト、漢字は2バイト)を前提としている
    コード処理がエラーとなる(UTF-8は漢字が3バイト)。EditorをSJISに変更した
    Eclipse のProject 欄にあるファイルを右クリックし、property を出す。最下段にフォント選択欄があるが
    sjis が無いので、sjis と入力し適用ボタンを押す。

    最初は全く気が付かずにあたふたしました。漢字コードをHex表示させて気が付きました。
    ただ、Eclipse Debug がエラーで動かないので、LCDで表示させなければならず手間でした。
    これはこれで直しておかねばならないが。

備忘録の備忘録
1.
LCDに文字を表示させるには、文字コードからフォントデータの配列インデックスを算出し、更にその配列インデックスからLCDのピクセルにドット打って作図するのだが、その仕組みは 後閑さんの picfun 記事にしっかりと記載されています。
こちらのページにはフォントデータの配列インデックスとLCD各ドットへの点位置関係が図解されています。
ただし LCD が特殊なので、ドットを打つルーチンはこちらのページこのコードが直接的に参考になります。

2.
半角全角交じりの文字を全て全角に変換する機能は上記2.こちらのコード でOKになったのだが、当初は半角だけの文字列を全角にすると思い込んでいたので、自分で半角と全角を識別するルーチンを作った。
ここで問題を発生させたのが「文字列の定義の仕方」でした。c言語1年生レベルの小生が起しそうなミスでした。
簡単に書くと
 char *str = "ABC"; と記述する方法は「書き換えられない文字リテラル」を定義しており
 char str[] = "ABC"; と記述する方法は「書き換えられることができる文字リテラル」を定義することになる。

3.
同上の半角から全角に変換するコードには私が使ったことの無い for 文の使い方がありました。
コードを記述すると、
int han2zen(char *str)
{
        char *buf,*p,*ptr;
        buf=(char *)calloc(strlen(str)*2+1,sizeof(char));
        for(ptr=str,p=buf;*ptr!='\0';*ptr++){
            if(issjiskanji(*ptr)){
                    /* SJIS漢字1バイト目の場合 */
                    *p=*ptr;
                    p++;
                    ptr++;
                    *p=*ptr;
                    p++;
                    *p='\0';
                    continue;
            }
                switch((int)*ptr){
                        case    ' ': strcpy(p," ");p+=2;break;
                        case    '!': strcpy(p,"!");p+=2;break;
         ・・・・・中略・・・・・・・・・・・・・・・・・・・・・・・
                        case    '}': strcpy(p,"}");p+=2;break;
                        default:
                                *p=*ptr;
                                p++;
                                *p='\0';
                                break;
                }
        }
        strcpy(str,buf);
        free(buf);
        return(0);
}
私なりに解釈すると(ptr=str)関数han2zenに渡された文字列 str アドレスを ptr に、(p=buf)buf アドレスを
p に引き継ぐ。(*ptr != '\0') 文字列の最後でなかったら、(*ptr++)文字列のアドレスを進める。巧妙だ。
しかし、その後に繋がる
                    *p=*ptr;
                    p++;
                    ptr++;
                    *p=*ptr;
                    p++;
                    *p='\0';
p++やptr++はアドレスを増やすものと理解できるが、*p=*ptr はどういう意味なのか理解出来ていません。
そもそも*というポインタ記号が付く場合(for文内の*ptr++)と、付かない場合(上記 ptr++)の違いが
解っていない自分であります。

尚、本課題を進めるについて本文中で紹介したページ以外に、以下のページを参照しました。
マイコンでフォントを読み込む!!
チカラの技術、ビットマップフォントでLCDにファイル名を表示
マイコン風雲録、FONTX2ビューワ
半角と全角の混在するShiftJIS文字コードの扱い方(C/C++)


理解していないところが多々ありますが、追々調べるとして、次はファイルリストにタッチして画面を次に送る
ことや、音楽再生させることかな。それには画面デザインも考える必要がありそうです。





テーマ : 電子工作・電気関係工作
ジャンル : 趣味・実用

プロフィール

haiga

Author:haiga
私のブログへようこそ!
電気オンチが始めた自作オーディオです
2010/3/17 電子工作をプラスしました。

自作オーディオの楽しみ共有のため、私が作ったパーツ提供をしてます。質問や要望を遠慮なくコメント欄に書き込んでください。

FC2カウンター
その日1回目にアクセスいただいた方の総カウントです
最新記事
最新コメント
最新トラックバック
カテゴリ
月別アーカイブ
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。