楽譜再生スケッチをCH552に移植してみた。 [Arduino]
以前につくった楽譜再生スケッチをCH552用に移植してみました。
音楽は、パッヘルベルのカノンのリンナイお風呂バージョンを3和音アレンジにしてみました。
CH552での感想
・プログラムサイズが大きくなると各所で書いてあったが、かなり大きくなり驚いた。
・いままで256クロックで4和音行けていたのが、2和音しか行けなくなった。
どうやら、プログラムサイズ的にも速度的にも、コンパイラの効率がわるいみたい。
拙いスケッチです。
"note2.h" は以下参照
Digisparkで音楽演奏【楽譜編】
https://hello-world.blog.ss-blog.jp/2022-07-04
(参考にさせていただいたところ)
ch55xduinoでタイマ割り込み|akita11|note
https://note.com/akita11/n/ne4f2a2fb7c70
音楽は、パッヘルベルのカノンのリンナイお風呂バージョンを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