SSブログ

楽譜再生スケッチをCH552に移植してみた。 [Arduino]

以前につくった楽譜再生スケッチをCH552用に移植してみました。
音楽は、パッヘルベルのカノンのリンナイお風呂バージョンを3和音アレンジにしてみました。

CH552での感想
・プログラムサイズが大きくなると各所で書いてあったが、かなり大きくなり驚いた。
・いままで256クロックで4和音行けていたのが、2和音しか行けなくなった。
どうやら、プログラムサイズ的にも速度的にも、コンパイラの効率がわるいみたい。

拙いスケッチです。
//  CH552 version Score Replay Sketch
//    boards manager URL : https://raw.githubusercontent.com/DeqingSun/ch55xduino/ch55xduino/package_ch55xduino_mcs51_index.json
//    Board : CH55x Boards > CH552 Board
//    Clock : 24/16/12 MHz
#if !defined(CH552) && !defined(CH551)
#error "Only compatible with CH552 and CH551"
#endif

#include "notes2.h"                     // Definition data of notes ( pitch (scale & octave), and note value (length))
#define MAX_TRACK   4                   // Maximum number of tracks

// Number of cycles that are the wavelength of the basic 12-note scale (from C0 to B0) (with an interrupt every 512 clocks)
#if   F_CPU == 24000000L
  static const uint16_t CYC_SCALE[] = { 2867, 2706, 2554, 2411, 2275, 2148, 2027, 1913, 1806, 1705, 1609, 1519 };
#elif F_CPU == 16000000L
  static const uint16_t CYC_SCALE[] = { 1911, 1804, 1703, 1607, 1517, 1432, 1351, 1276, 1204, 1136, 1073, 1012 };
#elif F_CPU == 12000000L
  static const uint16_t CYC_SCALE[] = { 1433, 1353, 1277, 1205, 1138, 1074, 1014,  957,  903,  852,  804,  759 };
#endif

static const uint16_t Rinnai[] PROGMEM = {  //  "Canon in D" by Johann Pachelbel
  110 ,   0 ,
  a5e , F5s , g5s , a5e , F5s , g5s,  a5s , a4s , b4s , C5s , d5s , e5s , F5s , g5s ,
  F5e , d5s , e5s , F5e , F4s , g4s , a4s , b4s , a4s , g4s , a4s , d5s , C5s , d5s ,
  g4e , b4s , a4s , g4e , F4s , e4s , F4s , e4s , d4s , e4s , F4s , g4s , a4s , b4s ,
  g4e , b4s , a4s , b4e , C5s , d5s , C5s , d5s , e5s , F5s , g5s , a5s , b5s , C6s ,
  F5s , a5s , d6qt| LNh ,   0 ,
  F4q , d5q , C5h , RSq , b4q , d5h , d4hd, d4q , d4q , g4q , e4q , C5q , d5w ,   0 ,
  d4q , F4q , e4q , a2q , b2q , d4q , F4q , F2q , b3q , g2q , a3q , d3q , RSq , g2q , C4q , a2q , d4w ,   0 ,   0 };

void play552PWM(const uint16_t*);

void setup(){
  // *** PWM pin setteings (P1.5, PWM) *** = pinMode( 15, OUTPUT ); but you can save about 500 bytes
    PIN_FUNC  &= ~(bPWM1_PIN_X);        // Pin Function Select Register: 0(PWM1 use P1.5)
    P1_MOD_OC &= ~(1<<5);               // P1 output mode register: 0(pull output)
    P1_DIR_PU |=   1<<5 ;               // P1 direction control and pull-up enable register: 1(output)
  // *** Register Settings (PWM1 for DAC)  ***
    PWM_CK_SE  = 1;                     // PWM_CK_SE(PWM clock divided Frequency setting register)
    PWM_CTRL  |= bPWM1_OUT_EN;          // PWM_CTRL: PWM1 Output Enable
  // *** Register Settings (TIMER1 for Data) *** Data processing interval 31.25kHz(768clk@24MHz)
    TR2  = 0;                           // T2CON: Timer2 start/stop bit: stop (for initial setting)
    C_T2 = 0;                           // T2CON: Timer2 clock source select bit: using the internal clock
    T2MOD |= bTMR_CLK | bT2_CLK;        // T2MOD: Set Fsys(24/16/12MHz) to Timer2 clock
    RCAP2H = 0xfe;  RCAP2L = 0x00;      // RCAP2(reload count/capture data register 2): Reload value on overflow(65536-512=65024=0xfe00, 24MHz/512=46,875Hz (Interrupt frequency))
    TH2  = RCAP2H;  TL2  = RCAP2L;      // T2COUNT: Timer2 counter, Initialize Timer2 count value with reload value (RCAP2)
    TF2  = 0;                           // T2CON: Timer2 overflow interrupt flag
    TR2  = 1;                           // T2CON: Timer2 start/stop bit: start
}

void loop(){
  play552PWM( Rinnai );                 // Playback
  delay(5000);
}

void play552PWM(const uint16_t *d){     // CH552 PWM version (24/16/12 MHz support)
  uint8_t  Tracks;                      // Number of tracks
  const uint16_t *NoteP[MAX_TRACK];     // Pointer for each track of the score
  uint16_t NoteCycle, n;                // Number of interrupt cycles required for the length of the reference note (96th note) and its counter (n)
  uint16_t note = 0;                    // Note (pitch + note value) information read from PROGMEM
  uint8_t  len[MAX_TRACK];              // Length of note (how many 96th notes) (subtraction counter)
  uint16_t cyc[MAX_TRACK], c[MAX_TRACK];// Number of interrupt cycles (cyc) required for one sound wavelength cycle and its counter (c)
  uint16_t env[MAX_TRACK];              // Sound amplitude (envelope)
  uint16_t out[MAX_TRACK];              // Output value
  uint8_t  lap = 20;                    // Sound transitional cycles(laptime) (for Attenuation calculator)

  // *** Preparing Scores ***
  NoteCycle = F_CPU /512 *4 *60 / *d++ /MIN_NOTE;       // Number of cycles required for reference note
  for( Tracks = 0; Tracks < MAX_TRACK; ) {              // Get the number of tracks and the start position of each track from the song data
    if( *d++ != 0 ) continue;           // Skip until the break comes
    if( *d   == 0 ) break;              // If two zeros follow, end of data
    len[ Tracks ] = 1;                  // Initialize the note length subtraction counter to the remaining 1
    NoteP[ Tracks++ ] = d;              // Get location in memory, Count up the number of tracks
  }
  n = Tracks;                           // Initialize the score processing so that it can be performed immediately after the start of the do loop

  // *** Playback ***
  do {
    // * Processing Scores *
    if( --n < Tracks ) {                // Processing of score for each reference note length
      if( !--len[n] ) {
        note   = *NoteP[n]++;
        len[n] = (uint8_t) note;        // The lower 8 bits are the length of the note (how many times the length of a 96th note)
        cyc[n] = (note>>8) ? CYC_SCALE[ (note>>8) & 0xf ] >> (note>>12) : 0;    // Upper 4 bits are octave, next 4 bits are pitch class (0-11), 0 for rests
          c[n] = 0;                     // Initialize the counter for the number of cycles to create one square wave cycle
        env[n] = 0xffff;                // Initially, the maximum amplitude
      }
      if( !n ) n = NoteCycle;
    }
    // * Waveform Processing / Output *
    switch( Tracks ) {                  // Create output data, Square wave with duty ratio of 1:1
      case 4:   out[3] = ( ( c[3] = (++c[3]==cyc[3]) ? 0 : c[3] ) < (cyc[3]>>1) ) ? env[3] : 0;  //   [[fallthrough]];  // C++
      case 3:   out[2] = ( ( c[2] = (++c[2]==cyc[2]) ? 0 : c[2] ) < (cyc[2]>>1) ) ? env[2] : 0;  //   [[fallthrough]];  // C++
      case 2:   out[1] = ( ( c[1] = (++c[1]==cyc[1]) ? 0 : c[1] ) < (cyc[1]>>1) ) ? env[1] : 0;  //   [[fallthrough]];  // C++
      case 1:   out[0] = ( ( c[0] = (++c[0]==cyc[0]) ? 0 : c[0] ) < (cyc[0]>>1) ) ? env[0] : 0;
    }
    switch( Tracks ) {                  // Change output by number of tracks
      case 1:   PWM_DATA1 =   out[0] >>8;                                   break;
      case 2:   PWM_DATA1 = ((out[0]>>1) + (out[1]>>1)) >>8;                break;
      case 3:   PWM_DATA1 = ((out[0]>>1) + (out[1]>>2) + (out[2]>>2)) >>8;  break;
      case 4:   PWM_DATA1 = ((out[0]>>2) + (out[1]>>2) + (out[2]>>2) + (out[3]>>2)) >>8;
    }
    switch( --lap ) {                   // Amplitude attenuation (Distribute processing per loop)
      case 4:   env[3] -= (env[3]>>9);    break;
      case 3:   env[2] -= (env[2]>>9);    break;
      case 2:   env[1] -= (env[1]>>9);    break;
      case 1:   env[0] -= (env[0]>>9);    break;
      case 0:   lap = 30;               // every 640usec(21.3usec*30) @24MHz (Adjustable)
    }
    while( !TF2 );                      // Waiting for Timer1 overflow (every 21.3usec(=512clk/24MHz))
      TF2 = 0;                          // Clear Timer1 Overflow Flag
  } while( note );                      // Exit if note data is 0
  PWM_DATA1 = 0;                        // Set output to 0
}

"note2.h" は以下参照
Digisparkで音楽演奏【楽譜編】
https://hello-world.blog.ss-blog.jp/2022-07-04



(参考にさせていただいたところ)
ch55xduinoでタイマ割り込み|akita11|note
https://note.com/akita11/n/ne4f2a2fb7c70
nice!(0)  コメント(0) 

CH552 Core Board と Ch55xduino [Arduino]

CH552Eと遊んでいたら、なにかの拍子にUSBから認識されなくなってしまいました。
しかし、こんなこともあろうかと CH552 Core Board というものを購入しておいたのでした。

ch552coreboard.jpg

このボードとCH552のデータシートとCh55xduinoの内容を確認しながら、Ch55xduino でのタイマー、カウンタ、PWMなどについて調べてみました。

Ch55xduinoのcoreのある場所 (windows)
%localappdata%\Arduino15\packages\CH55xDuino\hardware\mcs51\0.0.16\cores\ch55xduino

Ch55xduinoでは、、
・Timer0 : mills(), micros()などの時間関係に使用されている
・Timer1 : UART0 のところで、"use Timer1 for baudrate generator" って書いてあった
・AVRと異なり、タイマー・カウンタとPWMとは別々っぽい
・内部クロック(または外部オシレータ)24MHz(Fosc)で、そこから4倍の96MHz(Fpll)として、Fpllを2分周した48MHzをUSBへ、Fpllを分周設定したものをシステムクロック(Fsys)としている(187.5kHz~24MHz)
・PWMはFsysからの分周数を設定できる(0~255)
・タイマーの分周は、Fsysからの1/4/12分周の三択っぽい

他、気になったところ
・CH552/551のプログラム(ROM)は200回しか書き換えできない (Data Flash は約 10,000 回)。
・CH559のROMは10万回消せるっぽい。
タグ:CH552 Ch55xduino
nice!(0)  コメント(0) 

CH552Eで遊ぶ [Arduino]

ぼくがdigisparkやATtiny85で遊んでいたころ、世間はATtiny202という安価なマイコンで沸いていたらしい。
触ってみたかったけど、半導体不足のせいか秋月電子では売り切れ。
ちょっと割高なところで購入してみた。

digispark, ATtiny85と比較して、、
・とにかく安い
・しかし、売ってない
・UPDIでの書き込みが便利(USB-TTLにダイオードちょい足しするだけ)
・自由なピンが5つ
・フラッシュメモリ、RAMはATtiny85ほどはない
・USBに直接はつながらない
・レジスタがいままでのとちょっと違う

UPDIで書き込みするとして、電源だけUSBからもらう基板を考えてみた。
type-c_at202_updi.jpg

なら、ついでに安いUSB-TTL変換IC(CH340Eとか)も載せちゃったほうが便利じゃん、、と調べていたら、CH552とかいうICを見つけちゃいました。
USBに対応していて、キャパシタ2つつけるだけでよくて、フラッシュメモリも16kB, RAM 1280byteと十分。
いろいろなパッケージがあるのですが、そんなに足はいらないので一番小さいCH552Eを選択。
ということでAliExpressでお取り寄せ。
DIP変換基板に載せます。
かなり細かいのではんだ付け大変。
フラックスでひたひたにしている動画を参考に、フラックスまみれにしてはんだ付けしました。
これをUSB-Cレセプタクル基板(CCの5.1kΩ抵抗なし)と合体させてテストボードを作りました。
ch552e_test1.jpg
ch552e_test2.jpg

ボードマネージャに以下のURLを追加
https://raw.githubusercontent.com/DeqingSun/ch55xduino/ch55xduino/package_ch55xduino_mcs51_index.json

ドライバのインストールはよくわからなかかった。
解説のページとは異なり、unknown device #1 (だったと思う)というものが出てきた。
よくわからなかったけど、Zadigで、"libusb-win32"をインストールしてみた。
スケッチ書き込めた。(C++ではなくC言語らしいので、ちょっと修正が必要だった)


(参考)
★ATtiny202など
AVRマイコン ATTINY202-SSNR: マイコン関連 秋月電子通商-電子部品・ネット通販
https://akizukidenshi.com/catalog/g/gI-15731/

GitHub - SpenceKonde/megaTinyCore: Arduino core for the tinyAVR 0/1/2-series -
https://github.com/SpenceKonde/megaTinyCore

ここ↑から、Installation.md へいくと、追加のボードマネージャのURLがある。
http://drazzy.com/package_drazzy.com_index.json

USB Serial AdapterとUPDIの接続は、
AVR-Guidance/jtag2updi.md at master · SpenceKonde/AVR-Guidance · GitHub
https://github.com/SpenceKonde/AVR-Guidance/blob/master/UPDI/jtag2updi.md

★CH55xduino
GitHub - DeqingSun/ch55xduino: An Arduino-like programming API for the CH55X
https://github.com/DeqingSun/ch55xduino

追加のボードマネージャのURL
https://raw.githubusercontent.com/DeqingSun/ch55xduino/ch55xduino/package_ch55xduino_mcs51_index.json

CH55xをArduinoIDEで使う - Qiita
https://qiita.com/akita11/items/d7baed4ca3c06e292637


CH55xでどうでしょう

CH55xでどうでしょう

  • 作者: 秋田純一
  • 出版社/メーカー: ラトルズ
  • 発売日: 2022/12/27
  • メディア: 単行本(ソフトカバー)


タグ:CH552 Ch55xduino
nice!(0)  コメント(0)