代码;
/*
《AVR专题精选》随书例程
3.通信接口使用技巧
项目:使用AVR定时器1和外中断实现全双工软件串口
文件:softuart.c
说明:软件串口驱动文件
作者:邵子扬
时间:2012年12月16日
*/
#include "softuart.h"
// 内部变量
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;
// 定时器初始化
// CTC Mode
// 分频比: 1
OCR1A = 1;
ICR1 = (F_CPU / sfBAUDRATE) - 1;
TCCR1A = 0x00;
TCCR1B = (1 << WGM13)|(1 << WGM12)|(1 << CS10);
TIMSK = (1 << OCIE1A)|(0 << OCIE1B);
// 外中断初始化
// INT1
// 允许外中断1,下降沿触发方式
MCUCR = (1 << ISC11);
sf_ENABLE_RXINT();
}
// 检查数据接收标志
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_TXDsvr(void)
{
switch(stUART.TXDcnt)
{
case 0:// 无数据
return;
case 1:// 发送起始位
stUART.TXDcnt++;
PINCLR(sfUART_TXDIO);
break;
case 2:// 发送数据
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;
break;
default:
stUART.TXDcnt = 0;
return;
}
}
// 软件串口数据接收服务程序
void sfUART_RXDsvr(void)
{
switch(stUART.RXDcnt)
{
case 0:// 接收起始位
stUART.RXDBUF = 0;
stUART.RXDcnt++;
break;
case 1:// 接收数据位
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
stUART.RXDBUF = stUART.RXDBUF >> 1;
if(PININ(sfUART_RXDIO))
stUART.RXDBUF |= 0x80;
stUART.RXDcnt++;
break;
case 9:// 停止位
stUART.sfRXC = 1;
sf_RXD_STOP();
GIFR |= (1<<INTF1);
sf_ENABLE_RXINT();
break;
default:
return;
}
}
main.c
/*
《AVR专题精选》随书例程
3.通信接口使用技巧
项目:使用AVR定时器1和外中断实现全双工软件串口
文件: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>
#define UBRRREG (F_CPU / ( 8 * sfBAUDRATE ) - 1)
// 定时器1 COMPA中断服务程序
ISR(TIMER1_COMPA_vect, ISR_NOBLOCK)
{
sfUART_TXDsvr(); // 软件串口发送服务程序
}
// 定时器1 COMPB中断服务程序
ISR(TIMER1_COMPB_vect, ISR_NOBLOCK)
{
sfUART_RXDsvr(); // 软件串口接收服务程序
}
// 外中断服务程序
ISR(INT1_vect)
{
sf_DISABLE_RXINT(); // 禁止外中断, 避免重复触发
sf_RXD_START(); // 启动接收程序
}
int main(void)
{
unsigned char tmp;
PORTB = 0xFF; // 仿真时, PB2(SS)需要设置为高电平
// 否则会出错. 实际使用时不需要
sfUART_init(); // 初始化软件串口
// 使用硬件串口作为对比
UBRRH = UBRRREG / 256;
UBRRL = UBRRREG % 256;
UCSRA = ( 1 << U2X );
UCSRB = ( 1 << TXEN );
UCSRC = ( 1 << UCSZ1) | ( 1 << UCSZ0 );
sei(); // 开中断
for(;;)
{
if(sfUART_RXC()) // 接收到新数据
{
tmp = sfUART_getbyte(); // 读取数据
UDR = tmp; // 发送到硬件串口
sfUART_sendbyte(tmp); // 发送到软件串口
}
if(sfUART_TXC()) // 数据发送完成
{
sfUART_clrTXC();// 清除发送完成标志
}
}
return 0;
}
仿真效果图: