ATtiny2313を100個買うのが流行っている(3)


そんなわけで、ATmega168用に書いたシリアル送受信のプログラム(http://d.hatena.ne.jp/yaneurao/20080713)をATtiny2313に移植してみた。


・ATtiny2313はRAMが少ないので送受信の受信バッファをそれぞれ32byteにした。
・ATtiny2313はFlashもそれほど大きくないので送信/受信のいずれかが不要ならコンパイルしないようにifdefを入れた。
・特にテクニックらしいことはしていないが


UCSRB = 0
#ifdef __RECEIVE_ENABLE__
| (1<<RXEN) | (1<<RXCIE)
#endif
#ifdef __SEND_ENABLE__
| (1<<TXEN) | (1<<TXCIE)
#endif
;
とか、

while(!(((usart_send_write + 1) ^ usart_send_read) & 0x1f))
// usart_send_write+1 と usart_send_readの下位6bitが一致する間ループする
;
は、知らない人には「おお!」という感じかも知れない。知ってる人には、「当たり前だろ!」かも知れない。
・ソースは以下に貼り付けておくので自由にコピペして使ってください。(何かあっても責任は持ちませんが)



#ifndef __USART2313__H__
#define __USART2313__H__

// ATtinyはI/Oが少ないので場合によっては送信機能は使いたくない。
// このシンボルをdefineしなければ送信関係はコンパイルされない。
#define __SEND_ENABLE__

// 同じく、このシンボルをdefineしなければ受信関係はコンパイルされない。
#define __RECEIVE_ENABLE__

/* sio設定 */
void sio_init(unsigned int baud,int bit)
{
unsigned int ubrr = (((F_CPU>>4)+(baud>>1))/baud-1);
// UBRRを設定するときに丸め処理をしておく。

UBRRH = (unsigned char)(ubrr>>8); // ボーレート上位8bit
UBRRL = (unsigned char)ubrr; // ボーレート下位8bit

UCSRB = 0
#ifdef __RECEIVE_ENABLE__
| (1<<RXEN) | (1<<RXCIE)
#endif
#ifdef __SEND_ENABLE__
| (1<<TXEN) | (1<<TXCIE)
#endif
;
// 送受信許可,送受信割り込み許可
switch(bit)
{
case 8:
UCSRC = (3<<UCSZ0) ; // stopbit 1bit , 8bit送信
break;
case 5:
UCSRC = 0; // stopbit 1bit , 5bit送信
}
}

// byteを定義しておく。
typedef unsigned char byte;

// フロー制御をしないので32 bytesの送受信bufferを自前で用意する
#ifdef __RECEIVE_ENABLE__
volatile char usart_recvData[32]; // USARTで受信したデータ。ring buffer
volatile byte usart_recv_write; // 現在のwrite位置(usart_recvDataのindex)
byte usart_recv_read; // 現在のread位置(usart_recvDataのindex)
#endif
#ifdef __SEND_ENABLE__
volatile char usart_sendData[32]; // USARTで受信したデータ。ring buffer
byte usart_send_write; // 現在のwrite位置(usart_sendDataのindex)
volatile byte usart_send_read; // 現在のread位置(usart_sendDataのindex)
#endif

#ifdef __RECEIVE_ENABLE__

// データを受信しているかのチェック。受信しているなら非0。
int is_received()
{
return (usart_recv_write != usart_recv_read) ? 1 : 0;
// read位置とwrite位置が異なるならば受信データがあるはず
}

// データを受信するまで待機する
void wait_for_receiving()
{
while(!is_received())
;
}

// 受信したデータを返す。受信したデータがない場合は受信するまで待機。
int getReceivedData()
{
wait_for_receiving();
return usart_recvData[usart_recv_read++ & 0x1f];
}

// 割り込みによる受信
ISR(USART_RX_vect)
{
usart_recvData[usart_recv_write++ & 0x1f] = UDR; // 受信データを受信バッファに格納
}
#endif


#ifdef __SEND_ENABLE__

// 送信バッファにデータがあれば、そこから1バイト送信するルーチン。
// 内部的に使用しているだけなのでユーザーは呼び出さないで。
void private_send_char()
{
if (usart_send_write != usart_send_read)
UDR = usart_sendData[usart_send_read++ & 0x1f];// 送信バッファのデータを送信
}

// 割り込みによる送信
ISR(USART_TX_vect)
{
private_send_char();
}

// 1バイト送信
void sendChar(int c)
{
// 送信バッファがいっぱいなら待つ
while(!(((usart_send_write + 1) ^ usart_send_read) & 0x1f))
// usart_send_write+1 と usart_send_readの下位6bitが一致する間ループする
;

// 何はともあれ送信バッファにデータを積む。
usart_sendData[usart_send_write++ & 0x1f] = c;

// 送信レジスタがセットされている == 送信できる状態 ならば、
// 一度だけ送信しておく。
if (UCSRA & (1<<UDRE))
private_send_char();

// 例えば次のように送信バッファにデータを積まずにUDR0に直接アクセスするコードは
// よくない。
// if (UCSR0A & (1<<UDRE0))
// UDR0 = c;
// else
// usart_sendData[usart_send_write++] = c;
// これは、else句が実行される瞬間にUSART_TX_vectによる割り込みがかかり、
// usart_send_write == usart_send_readであった場合、次にsendCharが呼び出されて
// その送信が完了するまでここで積んだデータが送信されないからである。
}

// 文字列の送信
void sendString(char *p)
{
while(*p)
sendChar(*p++);
}
#endif

#endif