有两种情况使得普通I/O口模拟UART发送数据变得很有实用意义
1.我们使用了一个类似ATTINY26这样的没有UART接口的单片机,在调试阶段我们需要查看单片机内部的一些数据(例如A/D转换结果)时。
2.在设计中我们为了数据通信(例如RS485,CAN等)已经使用了仅有的一个UART接口,再没有一个很好的与计算机连接的接口时。
用软件实现UART功能时完全可能的,但这要消耗一定的处理器资源,通常实现一个完整的UART那么需要有一个(或两个)定时器,一个外部中断,两个普通的I/O口用于RXD和TXD。
出于调试目的的软UART没有必要实现发送和接收全功能,如果仅仅时想从计算机屏幕上查看类似A/D转换结果这样的数据,那么主要实现发送功能即可。下面时我在ATTINY26上实现的UART发送模块,它在自动标定的内部1MHz时钟频率下工作,发送格式为波特率:9600,8个数据位,1个停止位。
接口函数声明头文件:
//sim_uart.h
#ifndef SIM_UART_H
#define SIM_UART_H
void suart_init(void);
void suart_write_byte(uint8_t dat);
#endif
源程序文件:
/******sim_uart.c 模拟UART发送程序**********
//内部校准的1MHz时钟下工作,发送波特率为9600
//本模块用PA6模拟UART的TXD引脚实现发送数据功能
//资源:一个CTC(比较匹配时清零)功能的定时器
//MCU:attiny26
//编译:WinAVR20070525
//芯艺设计室 http://www.chipart.cn
//2008-06-29
//本程序在实际硬件上调试通过!
********************************************/
#include <avr/io.h>
#include <avr/interrupt.h>
#include"main.h"
//模拟发送I/O端口操作定义
#define DEBUG_PORT_INIT DDRA|=_BV(PA6)
#define DEBUG_PORT_SET PORTA|=_BV(PA6)
#define DEBUG_PORT_CLR PORTA&=~_BV(PA6)
static volatile uint8_t g_EndFlag;//发送完成标记
static uint8_t g_SendCounter; //发送计数器
static uint8_t g_SendData; //发送数据
//功能初始化
void suart_init(void)
{
TCNT1=0;
TCCR1A=0;
//TC1计数到OCR1C的值时会自动清零并产生溢出中断
//产生中断的频率应等于发送位时间,即波特率的倒数
//TC1预分频为1,即不分频
//(1/9600)/(1/1MHz)=1000 000/9600=一个位时间内的计数值=104
OCR1C=104;
TIMSK=_BV(TOIE1); //定时器溢出中断允许
DEBUG_PORT_INIT; //发送用I/O口初始化为输出
DEBUG_PORT_SET; //默认状态为高
}
//定时器溢出中断处理函数
ISR(TIMER1_OVF1_vect)
{
switch(g_SendCounter)
{
case 0: //开始位
DEBUG_PORT_CLR;
break;
case 9: //停止位
DEBUG_PORT_SET;
break;
case 10: //发送完成
TCCR1B=0x80;//计数器停止
g_EndFlag=1;//设置完成标记
break;
default: //数据
if(g_SendData&0x01)
DEBUG_PORT_SET;
else
DEBUG_PORT_CLR;
g_SendData>>=1;
break;
}
g_SendCounter++;
LED_Y_FLASH;
}
//阻塞发送一个字节
void suart_write_byte(uint8_t dat)
{
g_EndFlag=0;
g_SendCounter=0;
g_SendData=dat;
TCNT1=0; //等待一个位时间后从中断开始发送
TCCR1B=0x81; //计数器开始计数,并与OCR1C匹配时清零
while(g_EndFlag==0); //等待发送完成
LED_Y_CLR;
}
|
|