ATmega328PのSPIペリヘラルを使わずにタイマーでSPIを出してみる。
Timer2のCTCモードでCompareAとCompareBを使って、任意のスピードでSPIを出すのが目標。
で、いきなりこうなる。
#define PIN_CSN (0x04) #define PIN_MOSI (0x08) #define PIN_MISO (0x10) #define PIN_SCK (0x20) volatile uint8_t txCompleted; volatile uint8_t trxCount; volatile uint8_t emutxSPDR; volatile uint8_t emurxSPDR; ISR(TIMER2_COMPA_vect){ emurxSPDR=emurxSPDR<<1; if(PINB&PIN_MISO){ emurxSPDR|=0x01; } PORTB|=(PIN_SCK); trxCount=trxCount+1; if(trxCount>15){ txCompleted=1; TCCR2B&=0xF8; TIMSK2=0x00; } } ISR(TIMER2_COMPB_vect){ uint8_t tempPORTB; tempPORTB=PORTB; if(emutxSPDR&0x80){ tempPORTB&=~PIN_SCK; tempPORTB|=PIN_MOSI; }else{ tempPORTB&=~PIN_SCK; tempPORTB&=~PIN_MOSI; } PORTB=tempPORTB; emutxSPDR=emutxSPDR<<1; trxCount=trxCount+1; if(trxCount>15){ txCompleted=1; TCCR2B&=0xF8; TIMSK2=0x00; } } uint8_t trxCount_ini[]={0x0E,0x0C,0x0A,0x08,0x06,0x04,0x02,0x00}; void Timer2CTC_SPI_MasterStart(uint8_t dataLength_in_bit){ trxCount=trxCount_ini[dataLength_in_bit]; DDRB|=(PIN_MOSI|PIN_SCK); TCCR2A=0x02; TCCR2B=0x00; TCNT2=0x00; TIFR2=0x07; TIMSK2=0x06; OCR2B=0x0C; OCR2A=0x18; TCCR2B|=0x02; } uint8_t txdata[]={0x55,0x50,0xAA,0xAF}; uint8_t byte_pos; void setup(void){ DDRB|=PIN_CSN; PORTB|=PIN_CSN; byte_pos=0; } void loop(void){ txCompleted=0; emutxSPDR=txdata[byte_pos&0x03]; PORTB&=~(PIN_CSN); Timer2CTC_SPI_MasterStart(8); while(txCompleted==0){ // } PORTB|=(PIN_CSN); byte_pos++; delayMicroseconds(60); }
SPI波形は出せている。
OCR2B=0x0C;
OCR2A=0x18;
の設定で40kHz。これを小さくすれば速いSPIになるんだけど、これ以上小さくすると、時々クロックパルスが抜けちゃいましたorz。だめだな、遅すぎる。割り込みでやっていることをもっと小さくすると速くなるのか?
で、こんなコードでやってみた。ピン上げ下げだけ。
#define PIN_SCK (0x20) ISR(TIMER2_COMPA_vect){ PORTB|=(PIN_SCK); } ISR(TIMER2_COMPB_vect){ PORTB&=~(PIN_SCK); } void setup(void){ DDRB|=(PIN_SCK); TCCR2A=0x02; TCCR2B=0x00; TCNT2=0x00; TIFR2=0x07; TIMSK2=0x06; OCR2B=0x08; OCR2A=0x10; TCCR2B|=0x02; } void loop(void){ }
約60kHzで、2msくらいに一回、パルスがおかしくなる。Timer0が悪さしてるのかな?とにかく、Arduino bootloaderの書かれているATmega328PのTimer2では正確で速いものは作れない。クソッ。
コメントをお書きください