ATtiny402マイコン サンプル:割り込みでシリアル出力
Arduino 環境でのATtiny402。
UNO R3と同じようにSerial.beginとSerial.print
を使えばシリアル入出力が可能です。
しかし・・・なにせROMが4kバイトしかありません。
できるだけメモリーを節約したい・・・
ということで、
・シリアル出力だけで受信はいらない。
・でも出力待ちはイヤ。
出力したらすぐ戻ってくるように割り込み
で処理したい。
こんなときのサンプルです。
/********************************/
/* 割り込みでシリアル送信を */
/********************************/
// 9600BPSでシリアル出力 受信はなし
// 1秒ごとに2chのA/D値をシリアル出力
/***** タイマデータ *****/
volatile byte tm_10ms; // 10msダウンカウントタイマ
/***** タイマーTCB0周期割り込み *****/
// 1ms割り込み
ISR(TCB0_INT_vect)
{
static byte c1; // 10msカウンタ
TCB0.INTFLAGS = 1; // 割込要求クリア
ADC0_COMMAND = 1; // A/D変換開始指令
VPORTA.OUT |= (1 << 3); // PA3 H (!!!) 7pin
c1++; // 10msチェック
if (c1 >= 10) {
c1 = 0;
if (tm_10ms) tm_10ms--; // 10msダウンカウント
}
}
/**** A/D データ *****/
// A/D変換チャンネル
const byte ad_mpx[] = {
0b00000001, // AIN1:PA1 4pin
0b00000010, // AIN2:PA2 5pin
};
// A/Dデータ(2ch)
volatile word ad_15b[2]; // A/D変換データ 0~32736(32回加算)
// 割込で書き込み
/***** A/D割り込み処理 *****/
ISR(ADC0_RESRDY_vect)
{
static byte ch; // A/D変換チャンネル(ch0,1)
ad_15b[ch] = ADC0_RES; // A/D (0~1023*32) 15bitデータ
ch++; // 次変換チャンネル
if (ch >= 2) ch = 0; // 0に戻す
ADC0.MUXPOS = ad_mpx[ch]; // A/D入力MPX切り替え
VPORTA.OUT &= ~(1 << 3); // PA3 L (!!!) 7pin
}
/***** シリアル出力バッファ *****/
char str_bff[16]; // 文字出力バッファ
/***** 送信割り込みデータ ******/
volatile char tx_bff[32]; // 送信割り込み文字バッファ
volatile byte tx_rdp; // 送信 読み出しポインタ
volatile byte tx_wrp; // 送信 書き込みポインタ
volatile byte tx_cnt; // 送信 データ数
/***** シリアル送信割り込み *****/
// 送信完了で起動
ISR(USART0_TXC_vect)
{
USART0.STATUS = USART_TXCIF_bm; // 送信完了解除
if(tx_cnt == 0){ // バッファに残データ無し
USART0.CTRLA &= ~USART_TXCIE_bm; // 送信完了割り込みオフに
}
else{ // バッファにデータあり
USART0.TXDATAL = tx_bff[tx_rdp]; // 1文字送信
tx_rdp++; // ポインタ+1
if(tx_rdp >= sizeof(tx_bff)){ // 最終?
tx_rdp = 0; // 先頭に
}
tx_cnt--; // データ数-1
}
}
/***** シリアル送信可チェック *****/
// 1:送信可 0:送信できない(バッファいっぱい)
byte txx(void)
{
byte cnt;
cli(); // 割込禁止に
cnt = tx_cnt; // 送信バッファ内データ数
sei();
if(cnt >= sizeof(tx_bff)) return 0; // 送信不可
else return 1; // 送信OK
}
/***** シリアル出力 *****/
void tx(char c)
{
while(!txx()); // 送信OK待ち
// 初めてならいきなり出力
// 2回目以降はバッファに書き込む
cli(); // 割込禁止に
if(!(USART0.CTRLA & USART_TXCIE_bm)){ // 初めての送信
USART0.CTRLA |= USART_TXCIE_bm; // 送信割り込みon
USART0.TXDATAL = c; // 1文字直接送信
}
else{ // 送信継続中
tx_bff[tx_wrp] = c; // 1文字書き込み
tx_wrp++; // 書き込みポインタ+1
if(tx_wrp >= sizeof(tx_bff)){ // 最終?
tx_wrp = 0; // 先頭に
}
tx_cnt++; // データ数+1
}
sei(); // 割込有効に
}
/***** 文字列出力 *****/
void txstr(const char *s)
{
while (*s != '\0') { // Nullまで
tx(*s); // 1文字出力
s++; // 次文字
}
}
/***** SETUP *****/
// 20MHzクロック
void setup()
{
cli(); // 割り込み禁止に
PORTA.OUT = 0b01000000; //ポート出力
PORTA.DIR = 0b11001000;
// || |||+---- PA0 6pin UPDI
// || ||+----- PA1 4pin in AIN1
// || |+------ PA2 5pin out AIN2
// || +------- PA3 7pin out
// |+---------- PA6 2pin out TXD
// +----------- PA7 3pin out
// タイマーA0, 分割モードで初期化されるので16bitモードに
TCA0.SPLIT.CTRLA = 0; // タイマー停止
TCA0.SPLIT.INTCTRL = 0; // 割り込みなしに
PORTMUX.CTRLC = 0; // TCAポート多重切り替えなしに
// タイマーB0 1msタイマー
TCB0.CCMP = 20000 - 1; // 20MHz/20000=1kHz
TCB0.CTRLB = 0b00000000;
// ||| +++---- CNTMODE 周期割り込み
// ||+-------- CCMPEN
// |+--------- CMPINT
// +---------- ASYNC
TCB0.CTRLA = 0b00000001;
// | | ||+---- ENABALE 有効
// | | ++----- CLKSEL 1/1 20MHz
// | +-------- SYNCUPD 同期
// +---------- RUNSTDBY
TCB0.INTCTRL = 1; // 割込許可
// A/D AIN1:PA1 4pin, AIN2:PA2 5pin
ADC0.CTRLA = 0b00000001;
// | ||+--- ENABLE 許可
// | |+---- FREERUN しない
// | +----- RESEL 10bit
// +---------- RUNSTDBY
ADC0.CTRLB = 0b00000101; // サンプル回数
// +++--- 32回 約680usの変換時間
ADC0.CTRLC = 0b01010011;
// ||| +++--- PRESC 20MHz/16=1.25MHz
// |++------- REFSEL VDD
// +--------- SMAPCAP 5PF
ADC0.CTRLD = 0; // 遅延なしに
ADC0.MUXPOS = 0b00000001; // MPX
// +++++--- AIN1 PA1
PORTA.PIN1CTRL = 0b00000100; // AIN1:PA1 4pin
PORTA.PIN2CTRL = 0b00000100; // AIN2:PA2 5pin
// | |+++---- ディジタル入力禁止
// | +------- pullupなし
// +----------- 反転I/Oなし
ADC0.INTCTRL = 0b00000001; // 割り込み
// |+---- RESRDY 変換完了割込許可
// +----- WCMP
// シリアル
USART0.CTRLA = 0; // 送信完了割り込みのセットはtx()で
USART0.CTRLB = 0b01000000;
// || ||||+-- MPCE
// || ||++--- RXMODE
// || |+----- ODME
// || +------ SFDEN
// |+-------- TXEN 送信有効
// +--------- RXEN 受信はしない
USART0.CTRLC = 0b00000011;
// |||||+++-- CHSIZE 8BIT
// ||||+----- SBMODE 1stop
// ||++------ PMODE Parity無し
// ++-------- CMODE 非同期USART
USART0.BAUD = 8333; // 4*20MHz/9600
sei(); // 割り込みイネーブル
}
/***** LOOP ******/
// 1秒ごとに2つのAD値をシリアル出力
void loop() {
byte i;
int d;
while (1) {
VPORTA.IN |= (1 << 7); // PA7 トグル (!!!) 3pin
if(tm_10ms == 0){ // タイムアップ?
tm_10ms = 100; // 1秒セット
for (i = 0; i < 2; i++) { // 2ch loop
cli(); // いったん割込禁止にして
d = ad_15b[i]; // A/D 15bit 32回累積加算値読み出し
sei();
d = d / 32; // 15bitを10bitに(0~1023)
// sprintf(str_bff, "%5d", d); // 5桁文字に
strcpy(str_bff, " ABC"); // ★
txstr(str_bff); // 文字列出力
}
txstr("\r\n"); // CR+LF
}
}
}
sprintfを使わなければROMが782バイト、RAMが58バイト
sprintf利用でROM 2559バイト
Serial.begin、Serial.printだとROM 1823バイトなので
ずいぶん軽量化しているかと。
もちろんArduino環境ではなく、Microchip Studioでの
プログラムでも使えます。
(setupやloopじゃなく)
10進数値出力だけで良いのなら
・ATtiny402マイコン サンプル:RCサーボモータテスター
で示したstrbcd()が役に立つかと。
ATtiny402の内蔵ハードウェア直叩きの例、
あとは液晶表示ルーチンあたりでしょか。
※注目点
・割り込みで処理されるデータ、可能なら1バイトに
してしまい、cli()、sei()で囲まなくてよいように。
・でも、2バイトのA/D値やらシリアル出力関連
レジスタを操作する時は割り込み禁止処理が必須。























最近のコメント