· 

ATTINY85でSPI Master

ATTINY85からSPIを撃ちます。Arduino化せずに、久しぶりにAtmel Studioを使う。とはいえ、ATtinyのUSIを使うをATTINY85でやっただけ。

とりあえず、Timer1を使ってみる。非同期動作で、タイマ1オーバーフロー割り込みでTCNT1を更新する。で、この更新する値がSPIのビットレートを決めている。まぁ最も簡単な方法。Timer1はデータシートに非同期動作とPWM動作しか記述がないので。

 

久しぶりにAtmel Studioを起動して、Projectを作成する。

適当なフォルダに適当な名前で

デバイスを選ぶ

こうなる。

そんなに悩まない部分の記録を無駄に記述してしまった。

#define F_CPU 8000000L

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define PIN_DI (0x01)
#define PIN_DO (0x02)
#define PIN_USCK (0x04)
#define PIN_SS (0x10)

volatile uint8_t txCompleted;
volatile uint8_t rxUSIDR;
volatile uint8_t tcnt1_bottom=0xF5;

ISR(TIM1_OVF_vect){
    USICR|=0x01;/*Toggle Clock Port Pin*/
    TCNT1=tcnt1_bottom;
}

ISR(USI_OVF_vect){
    TIMSK=0x00;/*disable Timer/Counter1 Interrupt*/
    TCCR1&=~(0x0F);/*stop timer sourcing*/
    USISR=0x40;/*Write 1 to clear Counter Overflow Interrupt Flag*/
    txCompleted=1;
    rxUSIDR=USIDR;
}

void USI_SPI_MasterInit(void) {
    DDRB|=(PIN_DO|PIN_USCK|PIN_SS);
    DDRB&=~(PIN_DI);
    PORTB|=(PIN_DI|PIN_USCK|PIN_SS);
    USICR=0x5A;
    
    TCCR1=0x00;
    GTCCR=0x00;
    TCNT1=0x00;
}

void USI_SPI_MasterTransmit(uint8_t data) {
    USIDR = data;
    txCompleted=0;
    TCNT1=tcnt1_bottom;
    TIFR=0x04;
    TCCR1=0x01;
    TIMSK=0x04;
}

int main(void) {
    USI_SPI_MasterInit();

    while (1) {
        sei();
        PORTB&=~PIN_SS;
        USI_SPI_MasterTransmit(0xC5);
        while(txCompleted==0){
            //
        }
        PORTB|=PIN_SS;
        cli();
        _delay_ms(1);
    }
}

ATTINY85は足が8本しかなくて、リセットピンも書き換えのためにnRESET機能から変更しないので、結局自由な足は5本しかない。SPIを撃つには4本必要なので、内蔵オシレータで動作させるしかない。

で、上の絵の通りUSIでUSCK, DO, DIが占領されていて、PB4をCSNピンとして使うってコードになってます。

 

ビルド。

回路はこんなん。自作の謎回路。wsnakさんから購入したPCBで作ったFT232RLインターフェースは今回は電源鳥だけに使ってます。XTAL1,2端子にセラロック挿しっぱなしなのはたんに外さなくても動くので外さなかっただけ。

書き込みウィンドウを開く。

ライターを選んでーデバイスを選んでーApply押してーRead押してーMemories押してーProgram押す。この方法しか知らんけど、ワンボタンででけんのかなっていつも思う。

 

ちなみに

tcnt1_bottom=0xF5

より大きい値にするとちゃんと動いてくれない。間に合わないんだろう。

で、約110kbps。

次は、Timer0のCTCモードでやってみる。

/*
 * spi_master_timer0_ctca.c
 *
 * Created: 2023/05/20 16:18:59
 * Author : hoge
 */ 

#define F_CPU 8000000L

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define PIN_DI (0x01)
#define PIN_DO (0x02)
#define PIN_USCK (0x04)
#define PIN_SS (0x10)

volatile uint8_t txCompleted;
volatile uint8_t rxUSIDR;
volatile uint8_t ocr0a_value=0x20;

ISR(TIM0_COMPA_vect){
    USICR|=0x01;/*Toggle Clock Port Pin*/
}

ISR(USI_OVF_vect){
    TIMSK=0x00;/*disable Timer/Counter1 Interrupt*/
    TCCR1&=~(0x0F);/*stop timer sourcing*/
    USISR=0x40;/*Write 1 to clear Counter Overflow Interrupt Flag*/
    txCompleted=1;
    rxUSIDR=USIDR;
    PORTB|=PIN_USCK;
}

void USI_SPI_MasterInit(void) {
    DDRB|=(PIN_DO|PIN_USCK|PIN_SS);
    DDRB&=~(PIN_DI);
    PORTB|=(PIN_DI|PIN_USCK|PIN_SS);
    USICR=0x5A;
    
    TCCR0A=0x02;//CTC Mode
    TCCR0B=0x08;
    GTCCR=0x00;
    TCNT1=0x00;
}

void USI_SPI_MasterTransmit(uint8_t data) {
    USIDR = data;
    txCompleted=0;
    OCR0A=ocr0a_value;
    TIFR=0x10;
    TCCR0B=0x01;
    TIMSK=0x10;
}

int main(void) {
    USI_SPI_MasterInit();

    while (1) {
        sei();
        PORTB&=~PIN_SS;
        USI_SPI_MasterTransmit(0xC5);
        while(txCompleted==0){
            //
        }
        PORTB|=PIN_SS;
        cli();
        _delay_ms(1);
    }
}

まぁこんな感じです。それこそATtinyのUSIを使うとほぼ同じです。

 ちなみに

ocr0a_value=0x20

より小さい値にするとちゃんと動いてくれない。間に合わないんだろう。

で、約120kbps。

追記)0x20でもおかしくなってる。クロック数が。0x28くらいじゃないとまともに動かない。結局約100kbps。

で、最終的なコードはこちら。

/*
 * spi_master_timer0_ctca.c
 *
 * Created: 2023/05/20 16:18:59
 * Author : hoge
 */ 

#define F_CPU 8000000L

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define PIN_DI (0x01)
#define PIN_DO (0x02)
#define PIN_USCK (0x04)
#define PIN_SS (0x10)

volatile uint8_t txCompleted;
volatile uint8_t rxUSIDR;
volatile uint8_t ocr0a_value=0x28;

ISR(TIM0_COMPA_vect){
    USICR|=0x01;/*Toggle Clock Port Pin*/
}

ISR(USI_OVF_vect){
    TIMSK=0x00;/*disable Timer/Counter1 Interrupt*/
    TCCR1&=~(0x0F);/*stop timer sourcing*/
    USISR=0x40;/*Write 1 to clear Counter Overflow Interrupt Flag*/
    txCompleted=1;
    rxUSIDR=USIDR;
    PORTB|=PIN_USCK;
}

void USI_SPI_MasterInit(void) {
    DDRB|=(PIN_DO|PIN_USCK|PIN_SS);
    DDRB&=~(PIN_DI);
    PORTB|=(PIN_DI|PIN_USCK|PIN_SS);
    USICR=0x5A;
    
    TCCR0A=0x02;
    TCCR0B=0x08;
    GTCCR=0x00;
    TCNT1=0x00;
}

void USI_SPI_MasterTransmit(uint8_t data) {
    USIDR = data;
    txCompleted=0;
    OCR0A=ocr0a_value;
    TIFR=0x10;
    TCCR0B=0x01;
    TIMSK=0x10;
}

int main(void) {
    USI_SPI_MasterInit();

    while (1) {
        sei();
        PORTB&=~PIN_SS;
        USI_SPI_MasterTransmit(0x01);
        while(txCompleted==0){
            //
        }
        PORTB|=PIN_SS;
        cli();
        _delay_us(100);
        sei();
        PORTB&=~PIN_SS;
        USI_SPI_MasterTransmit(0xDA);
        while(txCompleted==0){
            //
        }
        PORTB|=PIN_SS;
        cli();
        _delay_us(300);
    }
}