freetech

MC9S08FL16用IO口+定时器实现单线串行通信

0
阅读(3697)

一个特定的应用,要求用单线实现串行通信。其实原本可以用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 */
}