https://github.com/ti-nspire/AVR/tree/master/amRadio
タイマー0で搬送波を生成し、タイマー1から割り込みをかけて搬送波の出力をon/offする(要するに振幅変調のようなものをかける)。
#include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> #include "scale16.h" static inline void initTimer0(){ // タイマーカウンター0で1 MHzの搬送波を生成する。 TCCR0A |= (1 << WGM01); // CTCモードにする。 TCCR0A |= (1 << COM0B0); // OC0B (PD5)からトグル出力することにする。 TCCR0B |= (1 << CS00); // クロック周波数8 MHzを分周せずにカウントすることにする。 OCR0A = 3; // コンペア値Aを設定する。 // トグル周波数 = CPUクロック / (2 * 分周比 * (1 + コンペア値)) = 1 MHz } static inline void initTimer1(){ // タイマーカウンター1で搬送波をon/offする(振幅変調のようなものをかける)。 TCCR1B |= (1 << WGM12); // CTCモードにする。 TCCR1B |= (1 << CS10); // クロック周波数8 MHzを分周せずにカウントすることにする。 TIMSK1 |= (1 << OCIE1A); // コンペア値Aに一致したときに割り込んで、対応するISRを実行する。(※) // (※)タイマーカウンター1の値がコンペア値Aに一致すると、 // TIFR1 (Timer/Counter1 Interrupt Flag Register)レジスタの // OCF1A (Timer/Counter1 Output Compare A Match Flag)ビットがセットされて、 // 対応する割り込みベクタTIMER1_COMPA_vectが実行される。 } ISR(TIMER1_COMPA_vect){ // タイマーカウンター1がコンペア値Aに一致したときに、 DDRD ^= (1 << PD5); // PD5 (OC0B)からトグル出力されていたらそれを停止し、停止していたら再開する。 // 要するにタイマーカウンター1がコンペア一致するたびに搬送波のon/offを切り換える。 } static inline void txNote(uint16_t note, uint16_t duration){ OCR1A = note; // 音程を決めるパルス幅を設定して、 sei(); // 割り込みを許可して、 for(int i=0; i<duration; i++){ // 一定期間だけ搬送波に振幅変調のようなものをかけて、 _delay_ms(1); } // 一定期間が過ぎたら、 cli(); // 割り込みを禁止して、 DDRD |= (1 << PD5); // 変調されない搬送波だけが出力される状態に戻す。 } int main(){ // don't tell me you love me const uint16_t Notes[] = {B1,E2,Dx2,B1, A1,E2,Cx2,A1,}; const uint16_t NumOfNotes = (sizeof(Notes) / sizeof(uint16_t)); // ノートの数を取得しておく。 initTimer0(); initTimer1(); while(1){ for(int i=0; i<NumOfNotes; i++){ txNote(Notes[i], 180); } } return 0; }
このような感じで振幅変調されている。