使用普通定时器产生半双工软件串口

代码:

/*
  《AVR专题精选》随书例程
  
  3.通信接口使用技巧

  项目:使用普通定时器和外中断实现半双工软件串口
  文件:softuart.c
  说明:软件串口驱动文件
        
  作者:邵子扬
  时间:2012年12月16日

*/
#include "softuart.h"

// 声明软件串口变量
volatile struct TSOFTUART stUART;

// 初始化软件串口
void sfUART_init(void)
{
  // 设置IO状态
  PINDIR(sfUART_TXDIO, PIN_OUTPUT);
  PINSET(sfUART_TXDIO);
  PINDIR(sfUART_RXDIO, PIN_INPUT);
  PINSET(sfUART_RXDIO);
  
  // 初始化内部变量
  stUART.TXDcnt = 0;
  stUART.RXDcnt = 0;
  stUART.sfTXC  = 0;
  stUART.sfRXC  = 0;
  stUART.sfMODE = 0;

  // 设置定时器参数
  // CTC Mode
  // 分频系数=1
  OCR2 = F_CPU / sfBAUDRATE - 1;
  TCCR2 = (1 << WGM21);
  sf_START_TMR();  // 启动定时器

  // 设置外中断
  // 允许外中断1,下降沿触发方式
  MCUCR = (1 << ISC11);
  sfUART_mode(sfUART_mode_RXD);

}

// 设置软件串口工作模式
void sfUART_mode(char mode)
{
  char itmp;

  itmp = __save_interrupt();   // 保存中断状态
  cli();                       // 关中断

  stUART.sfMODE = mode;        // 设置工作模式
  if(stUART.sfMODE == sfUART_mode_TXD)
  {
    sf_START_TMR();
    sf_DISABLE_RXINT();
  }
  else
  {
    sf_STOP_TMR();
    sf_ENABLE_RXINT();
  }
  __restore_interrupt(itmp);   // 恢复中断
}

// 检查数据接收标志
char sfUART_RXC(void)
{
  return stUART.sfRXC;
}

// 检查数据发送完成标志
char sfUART_TXC(void)
{
  return stUART.sfTXC;
}

// 清除数据发送完成标志
void sfUART_clrTXC(void)
{
  stUART.sfTXC = 0;
}

// 读取数据
char sfUART_getbyte(void)
{
  stUART.sfRXC = 0;     // 清除数据接收标志
  return stUART.RXDBUF;
}

// 发送数据
// 调用函数后,数据只是放入发送缓冲区
// 发送过程由软件串口服务程序完成
void sfUART_sendbyte(char dat)
{
  stUART.TXDBUF = dat;  // 数据放入缓冲区
  stUART.sfTXC = 0;     // 清除标志位
  stUART.TXDcnt = 1;    // 开始发送
}

// 软件串口服务程序
void sfUART_svr(void)
{
  // 数据发送
  if(stUART.sfMODE == sfUART_mode_TXD)
  {
    // send
    switch(stUART.TXDcnt)
    {
      case 0:// 无数据
        break;
      case 1:// 起始位
        stUART.TXDcnt++;
        PINCLR(sfUART_TXDIO); // 设置IO输出低电平
        break;
      case 2:// 8位数据, 先发送低位
      case 3:
      case 4:
      case 5:
      case 6:
      case 7:
      case 8:
      case 9:
        if(stUART.TXDBUF & 0x01)
          PINSET(sfUART_TXDIO);
        else
          PINCLR(sfUART_TXDIO);
        stUART.TXDBUF = stUART.TXDBUF >> 1;
        stUART.TXDcnt++;
        break;
      case 10:// 停止位
        stUART.TXDcnt = 0;
        PINSET(sfUART_TXDIO);
        stUART.sfTXC = 1;
      default:
        break;
    }
  }
  else
  {
    // 接收
    switch(stUART.RXDcnt)
    {
      case 0:// 起始位
        stUART.RXDcnt = 1;
        stUART.RXDBUFtmp = 0;
        break;
      case 1:// 采样数据
      case 2:
      case 3:
      case 4:
      case 5:
      case 6:
      case 7:
      case 8:
        stUART.RXDBUFtmp = stUART.RXDBUFtmp >> 1;
        if(PININ(sfUART_RXDIO))
          stUART.RXDBUFtmp |= 0x80;
        stUART.RXDcnt++;
        break;
      case 9:// 停止位
        stUART.sfRXC = 1;
        stUART.RXDcnt = 0;
        sf_STOP_TMR();      // 停止定时器
        stUART.RXDBUF = stUART.RXDBUFtmp;
        sf_ENABLE_RXINT();  // 允许外部中断, 可以接收后面的数据
        break;
      default:
        break;
    }
  }
}

// 定时器中断服务程序
ISR(TIMER2_COMP_vect, ISR_NOBLOCK)
{
  sfUART_svr();  // 需要调用软件串口服务程序

}

// 外部中断服务程序
ISR(INT1_vect, ISR_NOBLOCK)
{
  // 禁止INT1中断,直到接收完成,避免重复触发
  sf_DISABLE_RXINT();

  stUART.RXDcnt = 1;
  TCNT2 = OCR2 / 16;
  sf_START_TMR();  // 启动定时器, 接收数据
}

main.c

/*
  《AVR专题精选》随书例程
  
  3.通信接口使用技巧

  项目:使用普通定时器和外中断实现半双工软件串口
  文件:main.c
  说明:主程序,演示软件串口的使用方法
        
  作者:邵子扬
  时间:2012年12月16日

*/
#include "cfg.h"
#include "macromcu.h"
#include "softuart.h"

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>


int main()
{
  unsigned int UBRRREG;
  unsigned char tmp;

  sfUART_init();  // 软件串口初始化

  // 设置硬件串口作为对比
  UBRRREG = F_CPU / ( 8 * sfBAUDRATE ) - 1;
  UBRRH = UBRRREG / 256;
  UBRRL = UBRRREG % 256;
  UCSRA = ( 1 << U2X );
  UCSRB = ( 1 << TXEN );
  UCSRC = ( 1 << UCSZ1 ) | ( 1 << UCSZ0 );

  sei();  // 开中断

  sfUART_mode(sfUART_mode_TXD);  // 设置为发送模式
  sfUART_sendbyte('>');          // 发送提示字符
  _delay_ms(100);                // 延时100ms,等待发送完成
  sfUART_mode(sfUART_mode_RXD);  // 切换为接收模式

  for(;;)
  {
    if(sfUART_RXC())             // 是否接收到数据
    {
      tmp = sfUART_getbyte();    // 读取数据
      UDR = tmp;                 // 发送到硬件串口
      sfUART_mode(sfUART_mode_TXD); // 切换到发送模式
      sfUART_sendbyte(tmp);      // 通过软件串口发送
    }
    if(sfUART_TXC())             // 发送完成
    {
      sfUART_clrTXC();           // 清除发送完成标志
      sfUART_mode(sfUART_mode_RXD); // 切换回接收模式
    }

  }

  return 0;
}

仿真效果图:

相关推荐
时光の尘6 分钟前
C语言菜鸟入门·关键字·float以及double的用法
运维·服务器·c语言·开发语言·stm32·单片机·c
嵌入式大圣2 小时前
单片机结合OpenCV
单片机·嵌入式硬件·opencv
日晨难再3 小时前
嵌入式:STM32的启动(Startup)文件解析
stm32·单片机·嵌入式硬件
yufengxinpian4 小时前
集成了高性能ARM Cortex-M0+处理器的一款SimpleLink 2.4 GHz无线模块-RF-BM-2340B1
单片机·嵌入式硬件·音视频·智能硬件
__基本操作__5 小时前
历遍单片机下的IIC设备[ESP--0]
单片机·嵌入式硬件
网易独家音乐人Mike Zhou11 小时前
【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter)
c语言·python·单片机·物联网·算法·嵌入式·iot
zy张起灵11 小时前
48v72v-100v转12v 10A大功率转换电源方案CSM3100SK
经验分享·嵌入式硬件·硬件工程
lantiandianzi18 小时前
基于单片机的多功能跑步机控制系统
单片机·嵌入式硬件
哔哥哔特商务网18 小时前
高集成的MCU方案已成电机应用趋势?
单片机·嵌入式硬件
跟着杰哥学嵌入式18 小时前
单片机进阶硬件部分_day2_项目实践
单片机·嵌入式硬件