· 

SPIをTimer2で(ATmega328P)

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では正確で速いものは作れない。クソッ。