||
写了个软件模拟串口程序,哈哈,单片机可多串口了
coldra 发表于 5/11/2006 12:41:52 AM AVR 单片机 ←返回版面
找个双串口单片机真是麻烦,所以想做个软件串口玩玩
环境:
winAVR
M48,8MHz
9600,1,8,1
输出:用定时器控制普通IO口输出位
输入:用外部中断+定时器,判断位的宽度
好几天没休息好了,利用闲暇写的,也没找到别人的参考程序,不过终于算是可以工作了,其实还应该有很多其它的方法可以试一下,比如用PWM输出串行数据,用输入捕获接收数据,或定时查询,或用任意一个IO口中断,则每个引脚都有可能
现在还有些问题,全双工同时收发时发送偶尔出错,别的中断干扰所致吧,占用两个定时器有些浪费,以后再修改吧,最好加上各种波率。软件串口终究不如原串口好用,不过要求不苛刻的话可以一用,如果波特率改到4800应该会稳定许多
本程序为直接摘出部分,删了无关的部分,在此可能有些变量没用,或有段落遗漏,请谅
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <avr/wdt.h>
#define Sbit1() PORTD|=1<<PD1
#define Sbit0() PORTD&=~(1<<PD1)
volatile unsigned int
eep_ms,//毫秒计时
keytime, //等待时间
SoundOnTime; ////
volatile unsigned char
rdata,
key,
start=0,
keycode, //
*TxPoint,
rtime,
INT0_time, //中断次数
RxLength=0, //接收长度
RUDR, //摸拟串口接收的数据
TxLength, //串口发送数据长度
SUDR; //串口发送的数据
unsigned char arr[10],DispBuff[10];
void Initial_IO(void)//IO口初始化
{
DDRD = 0X82; //PD1串口输出,PD0串口输入,PD2模拟串口输入(INT0)
PORTD = 0X82; //PD1输出高电平
}
void Initial_INT0(void)
{
EICRA|=(1<<ISC00);//边沿触发
EIMSK|=1<<INT0;
}
void Initial_timer0(void) //定时器0初始化
{
TCCR0B|=(1<<CS01); //定时器0:8M/8分频
TIMSK0|=(1<<TOIE0);
}
void Initial_timer1(void)
{
TCCR1A=(1<<WGM11);
TCCR1B=(1<<WGM13)|(1<<WGM12)|(1<<CS11); //定时器1 频率1MHz
ICR1=1000;
TIMSK1|=(1<<TOIE1);
}
void Initial_timer2(void)
{
TCCR2B=(1<<CS22); //系统频率/64分频,8us
TIMSK2|=(1<<TOIE2); //中断允许
}
void Initial_WDR(void) //看门狗初始化
{
wdt_enable(WDTO_1S);
wdt_reset();
}
void Initial(void)
{
Initial_IO();
Initial_timer0();
Initial_timer1();
Initial_timer2();
Initial_INT0();
Initial_WDR();
sei();
}
/*启动串口发送*/
void SendData(unsigned char *P,unsigned char DataLength)
{
TxLength=DataLength;
TxPoint=P;
start=0;
}
int main (void)
{
Initial();
while(1)
{
wdt_reset();
if((rdata)&&(eep_ms>10))//收到数据延时10mS后启动发送,回送验证数据
{
key=0;
SendData(&DispBuff[0],9);//发送DispBuff[0]的9位数据
while(TxLength);//等待发送完成
rdata=0;
eep_ms=0;
}
}
}
/*定时器0,100us溢出中断*/
SIGNAL(SIG_OVERFLOW0)
{
TCNT0=151;//重载数据,计时区间为151---255,共104uS,一个位的时间
if(TxLength)//
{
if(start==0)
{
Sbit0();//起始位
SUDR=*(TxPoint++);
}
else
{
if((start<=8))
{
if(SUDR&(1<<(start-1)))Sbit1();//数据1
else Sbit0();//数据0
}
else Sbit1();//停止位
}
if(start<10)start++;
else
{
TxLength--;//一字节 发送完成,字节数减1
start=0;
}//
}
}
/*定时器1,1ms溢出中断*/
SIGNAL(SIG_OVERFLOW1)
{
eep_ms++;
}
/*定时器2*/
SIGNAL(SIG_OVERFLOW2)
{
sei();
if(INT0_time)//有数据
{
INT0_time=0;//中断次数清0
rdata=1;//置有数据标志
eep_ms=0;
if(RxLength<10)DispBuff[RxLength++]=RUDR;
}
if(rtime<4)rtime++;//字节间隔时间,间隔3个字节重新开始一帧
else RxLength=0;
}
SIGNAL(SIG_INTERRUPT0)//INT0,边沿触发中断
{
unsigned char temp,temp2=0;
static unsigned char pre_TCNT2,j=0;
if(INT0_time==0)//一个字节第一个下降沿中断,起始位开始
{
TCNT2=130;
pre_TCNT2=130;
RUDR=0xff;//接收的数据初值
j=0; //位数清零
INT0_time++;//中断次数加一
}
else
{
temp=TCNT2;
if(temp>pre_TCNT2)temp2=temp-pre_TCNT2;//取一个高/低电平的宽度
if(temp2>10)//滤过窄电平(干扰信号)
{
pre_TCNT2=temp;//记录前一次的时间值
temp=0;
while(temp2>13)//计算位的个数,约13为一个位(8*13=104uS)
{
temp2-=13;//
temp++;
}
if(temp2>6)temp++;//计算位的个数,一般13为一个位
if(INT0_time==1)temp-=1;
if(INT0_time&1)//奇数次中断
{
while(temp)//位0的个数
{
RUDR&=~(1<<j);//相关位置0
temp--;
j++;
}
}
else j+=temp;//偶数,位1的个数,跳过
INT0_time++;//中断次数加一
}
}
rtime=0;
}