MC9S08FL16用IO口+定时器实现单线串行通信
0赞一个特定的应用,要求用单线实现串行通信。其实原本可以用SCI做,但有人把板子画到不是UART的地方了,只能用另外的方式。可以用定时器+IO口的方式实现。本次应用数据比较简单:定长的帧,第一个数固定为0x56,8位数据无起始和停止位。下面就发送和接收2方面说明之。
一、发送:发送相对简单,按bit位定时发送数据位即可。
二、接收:接收涉及一个与发送端同步的问题,一般是在每bit位的中间采样可靠些(本例不涉及每bit多次采样的滤波算法)。FL16没有键盘中断,IRQ中断引脚无法做输出,所以选用了TPM1的输入捕获来处理帧起始的1->0。当识别到帧起始时启用定时器另一通道的输出比较中断,让其比较值比刚才的捕获值晚半个bit位的时间即实现了中点采样。
下面给出样例代码:
#include <hidef.h> /* for EnableInterrupts macro */
#include "derivative.h" /* include peripheral declarations */
#define DIR_OUT 1
#define COM_IO_PIN PTBD_PTBD4
#define COM_IO_DIR PTBDD_PTBDD4
#define COM_IO_PE PTBPE_PTBPE4
#define BUS_CLK 4000000 //Hz
#define BAUD_RATE 1000 //bps
#define TPM_MOD BUS_CLK/BAUD_RATE //TPM分频系数为一时成立
#define BUF_LEN 4
unsigned char com_rx_buf[BUF_LEN]; //通信接收缓冲区
unsigned char com_tx_buf[BUF_LEN]; //通信发送缓冲区
unsigned char rx_index;//接收bit位索引
unsigned char tx_index;//发送bit位索引
//功能:通信预接收,IO口使能下降沿中断,打开定时器,当收到中断时进入输入捕获中断服务程序
void InitTPM1()
{
TPM1C0SC = 0x08;//通道0输入捕获,通道中断禁止
TPM1C1SC = 0x10;//通道1输出比较,通道中断禁止
TPM1C2SC = 0x10;//通道1输出比较,通道中断禁止
TPM1MOD = TPM_MOD;
TPM1SC=0x08;//TPM1时钟源为总线时钟;分频系数为一;溢出中断禁止
TPM1CNTH=0;//任意时刻对TPM1CNTH或TPM1CNTL的写操作将计数寄存器的计数值复位(清零)
}
//功能:开启定时器溢出中断,准备通信接收
//接收第二步:打开输出比较中断,计半个bit位的时间,以保证以后采到的都在bit位中点
interrupt VectorNumber_Vtpm1ch0 void TPM1CH0_ISR()
{
unsigned int tmp;
tmp = TPM1C0V + TPM_MOD/2;
if( tmp > TPM_MOD )
{
tmp -= TPM_MOD;
}
TPM1C1V = tmp; //TPMC1V比刚捕获的TPM1C0V错后半个bit位,即半个定时周期
TPM1C0SC_CH0IE = 0;//关闭定时器捕获中断
TPM1C1SC_CH1IE = 1;//开启定时器比较中断
}
//功能:进一次中断接收1bit数据,完毕后关闭比较中断
//接收第三步:逐bit位接收
interrupt VectorNumber_Vtpm1ch1 void TPM1CH1_ISR()
{
static unsigned char dat;
static unsigned char bit_cnt = 0;
if( bit_cnt == 0 )
{
dat = 0;
}
dat = (dat<<1) | COM_IO_PIN;
bit_cnt++;
if( bit_cnt == 8 ) //接收够8个bit位,则放入接收缓冲区
{
bit_cnt = 0;
com_rx_buf[rx_index] = dat;
rx_index++;
if( rx_index >= BUF_LEN )//所有数据接收完毕
{
TPM1C1SC_CH1IE = 0;//关闭定时器比较中断
}
}
}
//功能:进一次中断发送1bit数据,完毕后关闭比较中断
interrupt VectorNumber_Vtpm1ch2 void TPM1CH2_ISR()
{
static unsigned char dat;
static unsigned char bit_cnt = 0;
if( bit_cnt == 0 )
{
dat = com_tx_buf[tx_index];
}
COM_IO_PIN = ( dat >> bit_cnt);
bit_cnt++;
if(bit_cnt == 8)//发送完1字节,bit_cnt清0,tx_index递增
{
bit_cnt = 0;
tx_index++;
if(tx_index >= BUF_LEN)
{
TPM1C2SC_CH2IE = 0;//关闭定时器比较中断
}
}
}
interrupt VectorNumber_Vtpm1ovf void TPM1OVF_ISR()
{
}
void StartSend()
{
tx_index = 0;
TPM1C2V = TPM1C0V;//和接收同步,单机测试时有意义。
COM_IO_DIR = 1;
TPM1C2SC_CH2IE = 1;//开启定时器比较中断,进中断后逐bit位发送
}
void StartReceive()
{
rx_index = 0;
COM_IO_DIR = 0;
COM_IO_PE = 1;
//接收第一步:打开输入捕获中断
TPM1C0SC_CH0IE = 1;
}
void delay()
{
long i;
for(i=0;i<3000;i++)
{
__RESET_WATCHDOG(); /* feeds the dog */
}
}
void main(void) {
EnableInterrupts; /* enable interrupts */
/* include your code here */
com_tx_buf[0] = 0x12;
com_tx_buf[1] = 0x34;
com_tx_buf[2] = 0x56;
com_tx_buf[3] = 0x78;
COM_IO_PIN = 1;
InitTPM1();
StartReceive();
StartSend();
for(;;) {
delay();
StartSend();
__RESET_WATCHDOG(); /* feeds the dog */
} /* loop forever */
/* please make sure that you never leave main */
}