代码:
/*
《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;
}
仿真效果图: