SCI送信用リングバッファをMC908QB8に移植した [プログラム三昧]
HCS08 Unleashedに掲載されていたSCI送信用リングバッファをMC908QB8に移植してみました。
起
某所で「リングバッファのサンプル・プログラムが無いか」と質問がありました。 「HCS08 Unleashed にあるよ」と、書こうとしてターゲットがMC908QB8であることに気が付きました。 そのままでは、使えないので、MC908QB8向けに移植します。
承
使用するハードウェアは、ほこりをかぶっていたDEMO908QB8デモボードです。 家中、ジャンパを探し回って、ようやく動かすことができました。
リングバッファの移植に必要なのは、HCS08とMC908QB8のレジスタ・ビットの対照表です。 移植するために書き換えたレジスタ・ビットは、以下のとおりです。
HCS08 | MC908QB8 | 概要 |
---|---|---|
SCI1C2_TIE | SCC2_SCTIE | 割り込み許可ビット |
SCI1S1_TDRE | SCS1_SCTE | 送信データ書き込み可能フラグ |
SCI1D | SCDR | 送信データレジスタ |
転
リング・バッファ以外の部分、たとえば、ボーレートの設定やSCIの起動などは、参考サイトにある文章を参考にさせていただきました。 って、これは自分で書いた文章だよ。
デバッガから起動すると動くのに、スタンドアローンでは様子が変わるという、ありがちな問題も発生しました。 私の場合、「Watch Dog (COP) に与える餌が間に合わなかった」のと「一部のポートがフラフラしていた」のが原因でした。
COPはディセーブル、全ポートにプルアップ、で解決です。
結
というわけで、プログラムの完成です。 内容は、デモボード上のSW1/SW2を定期的に見に行って、押されていたらSCIにメッセージを表示するというものです。 このプログラム自身は、リングバッファを使っている恩恵は全く受けないのですが、サンプルとしては十分でしょう。
#include <hidef.h> /* for EnableInterrupts macro */ #include "derivative.h" /* include peripheral declarations */ #define TX_BUF_SIZE (32) //-------------------------------------------------------------- // Variables. //-------------------------------------------------------------- #pragma DATA_SEG __SHORT_SEG MY_ZEROPAGE byte tx_buf_read_pointer; byte tx_buf_write_pointer; struct { char tx_buf_empty:1; } flags; #pragma DATA_SEG DEFAULT byte tx_buffer[TX_BUF_SIZE]; //-------------------------------------------------------------- // initOSC() : void // Initialize the Oscillator Module (OSC) // The BUSCLK is adjusted to 3.2MHz. // CGMXCLK (BUSCLKx4) is used by ESCI. // See p100 of MC68HC908QB8 Data Sheet, Rev.1 //-------------------------------------------------------------- void initOSC() { // load trim value to adjust BUSCLK to 3.2MHz OSCTRIM = Optional; // Adjust trim for internal OSC OSCSC_ICFS = 0b10; // Select 12.8MHz as internal OSC } //-------------------------------------------------------------- // initBaudRate() : void // Initialize the baud rate of ESCI to 1200bps. // The CGMXCLK is assumed as a 12.8MHz clock. // DETAIL: // SCP = 01 for BPD=3 // SCR = 011 for BD=8 // PDS = 101 for PD=6 // PSSB = 11110 for PDFA=0.9375 // Baud Rate = 12.8MHz / (64 * BPD * BD * (PD + PDFA)) // = 1201 baud // See p130 of MC68HC908QB8 Data Sheet, Rev.1 //-------------------------------------------------------------- void initBaudRate() { SCBR_SCP = 0b01; // BPD = 3 SCBR_SCR = 0b011; // BD = 8 SCPSC_PDS = 0b101; // PD = 6 SCPSC_PSSB = 0b11110; // PDFA = 0.9375 } //-------------------------------------------------------------- // initESCI() : void // Initialize the Enhanced Serial Communication Interface (ESCI) //-------------------------------------------------------------- void initESCI() { SCC1_ENSCI = 1; // Enables ESCI module SCC2_TE = 1; // Enables transmitter } //-------------------------------------------------------------- // isr_sctx() : void // Interrupt sevice routine for SCI-TX //-------------------------------------------------------------- void __interrupt VectorNumber_SCITransmit isr_sctx(void) { if (tx_buf_read_pointer == tx_buf_write_pointer) { SCC2_SCTIE = 0; // Disable the interrupt flags.tx_buf_empty = 1; // Set empty flag } else { if (SCS1_SCTE) { SCDR = tx_buffer[tx_buf_read_pointer]; tx_buf_read_pointer++; tx_buf_read_pointer &= TX_BUF_SIZE - 1; } } } //-------------------------------------------------------------- // sci_putc(byte) : void // Put a character into the ring buffer. //-------------------------------------------------------------- void sci_putc(const byte data) { SCC2_SCTIE = 0; // Disable the interrupt flags.tx_buf_empty = 0; tx_buffer[tx_buf_write_pointer] = data; tx_buf_write_pointer++; tx_buf_write_pointer &= TX_BUF_SIZE - 1; SCC2_SCTIE = 1; // Enable the interrupt } //-------------------------------------------------------------- // sci_puts(byte *) : void // Put a string into the ring buffer. //-------------------------------------------------------------- void sci_puts(const byte *s) { while (*s) { sci_putc(*s++); } } //-------------------------------------------------------------- // initQueue() : void // Initialize the queue related variable. //-------------------------------------------------------------- void initQueue(void) { tx_buf_read_pointer = 0; // Initialize READ pointer tx_buf_write_pointer = 0; // Initialize WRITE pointer flags.tx_buf_empty = 1; // Set empty flag } //-------------------------------------------------------------- // main() : void //-------------------------------------------------------------- void main(void) { word i; CONFIG1 = 0x01; // Disable COP PTAPUE = 0x3F; // Enable all port pull-up PTBPUE = 0xFF; // Enable all port pull-up initOSC(); // Initialize OSC module initBaudRate(); // Initialize baud rate initESCI(); // Initialize ESCI module initQueue(); // Prepare QUEUE EnableInterrupts; /* enable interrupts */ /* include your code here */ for(;;) { if (!PTA_PTA5) { sci_puts("SW1 PUSHED\r\n"); } if (!PTA_PTA4) { sci_puts("SW2 PUSHED\r\n"); } // a delay for (i = 0; i < 30000; i++) { } __RESET_WATCHDOG(); /* feeds the dog */ } /* loop forever */ /* please make sure that you never leave main */ }
おっと、忘れてた。 Project.prmを変更して"RAM"領域を広げておかないと、バッファが配置できなかったのでした。
/* This is a linker parameter file for the mc68hc908qb8 */ NAMES END /* CodeWarrior will pass all the needed files to the linker by command line. But here you may add your own files too. */ SEGMENTS /* Here all RAM/ROM areas of the device are listed. Used in PLACEMENT below. */ Z_RAM = READ_WRITE 0x0040 TO 0x007F; RAM = READ_WRITE 0x0080 TO 0x013F; ROM = READ_ONLY 0xDE00 TO 0xFDFF; ROM1 = READ_ONLY 0xFFB0 TO 0xFFBD; ROM2 = READ_ONLY 0xFFC2 TO 0xFFCF; /* INTVECTS = READ_ONLY 0xFFDE TO 0xFFFF; Reserved for Interrupt Vectors */ END PLACEMENT /* Here all predefined and user segments are placed into the SEGMENTS defined above. */ DEFAULT_RAM, /* non-zero page variables */ INTO RAM; _PRESTART, /* startup code */ STARTUP, /* startup data structures */ ROM_VAR, /* constant variables */ STRINGS, /* string literals */ VIRTUAL_TABLE_SEGMENT, /* C++ virtual table segment */ DEFAULT_ROM, COPY /* copy down information: how to initialize variables */ INTO ROM; /* ,ROM1,ROM2: To use "ROM1,ROM2" as well, pass the option -OnB=b to the compiler */ _DATA_ZEROPAGE, /* zero page variables */ MY_ZEROPAGE INTO Z_RAM; END STACKSIZE 0x30 VECTOR 0 _Startup /* Reset vector: this is the default entry point for an application. */
参考サイト
- 003A QB8でもシリアル通信
- ESCIの使い方なんて、すっかり忘れていました。 一番参考になったのは、その昔自分で書いた文章だったというオチ。
- ScTec
- HCS08 Unleashedの筆者のサイトです。 日本語でもなく、英語でもなく、スペイン語(だったっけ?)なのが辛いところです。 書籍のエラッタ、書籍に掲載されているソースコードも入手できます。
参考文献
HCS08 Unleashed: Designer's Guide to the HCS08 Microcontrollers
- 作者: Fabio Pereira
- 出版社/メーカー: Booksurge Llc
- 発売日: 2007/11/13
- メディア: ペーパーバック
質問でーす。
1.flags.tx_buf_emptyの使い方が良く判りません。
2.tx_buf_write_pointer &= TX_BUF_SIZE - 1;
はバッファサイズが2の乗数であると言う大前提が存在しますので、後々思わぬトラップにならないでしょうか。
by hamayan (2009-04-02 11:45)
もう一つ質問です。
ボーレートが高ければ問題が表面化し難いのですが、とんでもなく低い時、リングバッファへの書き込みで上書きチェックを行わない場合、変なデータが送られる事にならないでしょうか。
by hamayan (2009-04-02 11:54)
> flags.tx_buf_emptyの使い方が良く判りません。
HCS08 Unleashed のプログラムに書いてあったので、そのまま残しています。書籍のプログラムでも、セット・リセットするだけで参照されていません。
> tx_buf_write_pointer &= TX_BUF_SIZE - 1;
書籍のプログラムでは、if (tc_buf_write_pointer >= TX_BUF_SIZE) … となっていましたが、エラッタの記述(Tsuneo さんのお勧め)に従って変更しています。
> リングバッファへの書き込みで上書きチェックを行わない場合
もちろん、リングバッファがあふれたらデータは壊れてしまいます。書籍のプログラムでもオーバフロー検出は行われていなかったので、そのまま移植してあります。
ProcessorExpert を使っちゃえという、解決方法ももちろんあります。
by noritan (2009-04-02 14:25)