通用的IO模拟串口程序.docx
- 文档编号:24066480
- 上传时间:2023-05-23
- 格式:DOCX
- 页数:14
- 大小:28.25KB
通用的IO模拟串口程序.docx
《通用的IO模拟串口程序.docx》由会员分享,可在线阅读,更多相关《通用的IO模拟串口程序.docx(14页珍藏版)》请在冰豆网上搜索。
通用的IO模拟串口程序
通用的I/O模拟串口程序
程序匠人发表于2006-3-1423:
43:
00 阅读全文(2215)|回复(0)|引用通告(0)|编辑
通用的I/O模拟串口程序(适用于任何带有定时器的单片机)
//UART.C
//
//GenericsoftwareuartwritteninC,requiringatimersetto3times
//thebaudrate,andtwosoftwareread/writepinsforthereceiveand
//transmitfunctions.
//
//*Receivedcharactersarebuffered
//*putchar(),getchar(),kbhit()andflush_input_buffer()areavailable
//*Thereisafacilityforbackgroundprocessingwhilewaitingforinput
//
//ColinGittins,SoftwareEngineer,HalliburtonEnergyServices
//
//ThebaudratecanbeconfiguredbychangingtheBAUD_RATEmacroas
//follows:
//
//#defineBAUD_RATE19200.0
//
//Thefunctioninit_uart()mustbecalledbeforeanycommscantakeplace
//
//Interfaceroutinesrequired:
//1.get_rx_pin_status()
//Returns0or1dependentonwhetherthereceivepinishighorlow.
//2.set_tx_pin_high()
//Setsthetransmitpintothehighstate.
//3.set_tx_pin_low()
//Setsthetransmitpintothelowstate.
//4.idle()
//Backgroundfunctionstoexecutewhilewaitingforinput.
//5.timer_set(BAUD_RATE)
//Setsthetimerto3timesthebaudrate.
//6.set_timer_interrupt(timer_isr)
//Enablesthetimerinterrupt.
//
//Functionsprovided:
//1.voidflush_input_buffer(void)
//Clearsthecontentsoftheinputbuffer.
//2.charkbhit(void)
//Testswhetheraninputcharacterhasbeenreceived.
//3.chargetchar(void)
//Readsacharacterfromtheinputbuffer,waitingifnecessary.
//4.voidturn_rx_on(void)
//Turnsonthereceivefunction.
//5.voidturn_rx_off(void)
//Turnsoffthereceivefunction.
//6.voidputchar(char)
//Writesacharactertotheserialport.
#include
#defineBAUD_RATE19200.0
#defineIN_BUF_SIZE256
#defineTRUE1
#defineFALSE0
staticunsignedcharinbuf[IN_BUF_SIZE];
staticunsignedcharqin=0;
staticunsignedcharqout=0;
staticcharflag_rx_waiting_for_stop_bit;
staticcharflag_rx_off;
staticcharrx_mask;
staticcharflag_rx_ready;
staticcharflag_tx_ready;
staticchartimer_rx_ctr;
staticchartimer_tx_ctr;
staticcharbits_left_in_rx;
staticcharbits_left_in_tx;
staticcharrx_num_of_bits;
staticchartx_num_of_bits;
staticcharinternal_rx_buffer;
staticcharinternal_tx_buffer;
staticcharuser_tx_buffer;
voidtimer_isr(void)
{
charmask,start_bit,flag_in;
//TransmitterSection
if(flag_tx_ready)
{
if(--timer_tx_ctr<=0)
{
mask=internal_tx_buffer&1;
internal_tx_buffer>>=1;
if(mask)
{
set_tx_pin_high();
}
else
{
set_tx_pin_low();
}
timer_tx_ctr=3;
if(--bits_left_in_tx<=0)
{
flag_tx_ready=FALSE;
}
}
}
//ReceiverSection
if(flag_rx_off==FALSE)
{
if(flag_rx_waiting_for_stop_bit)
{
if(--timer_rx_ctr<=0)
{
flag_rx_waiting_for_stop_bit=FALSE;
flag_rx_ready=FALSE;
internal_rx_buffer&=0xFF;
if(internal_rx_buffer!
=0xC2)
{
inbuf[qin]=
internal_rx_buffer;
if(++qin>=IN_BUF_SIZE)
{
qin=0;
}
}
}
}
else//rx_test_busy
{
if(flag_rx_ready==FALSE)
{
start_bit=get_rx_pin_status();
//TestforStartBit
if(start_bit==0)
{
flag_rx_ready=TRUE;
internal_rx_buffer=0;
timer_rx_ctr=4;
bits_left_in_rx=
rx_num_of_bits;
rx_mask=1;
}
}
else//rx_busy
{
if(--timer_rx_ctr<=0)
{
//rcv
timer_rx_ctr=3;
flag_in=
get_rx_pin_status();
if(flag_in)
{
internal_rx_buffer|=rx_mask;
}
rx_mask<<=1;
if(--
bits_left_in_rx<=0)
{
flag_rx_waiting_for_stop_bit=TRUE;
}
}
}
}
}
}
voidinit_uart(void)
{
flag_tx_ready=FALSE;
flag_rx_ready=FALSE;
flag_rx_waiting_for_stop_bit=FALSE;
flag_rx_off=FALSE;
rx_num_of_bits=10;
tx_num_of_bits=10;
set_tx_pin_low();
timer_set(BAUD_RATE);
set_timer_interrupt(timer_isr);//Enabletimerinterrupt
}
char_getchar(void)
{
charch;
do
{
while(qout==qin)
{
idle();
}
ch=inbuf[qout]&0xFF;
if(++qout>=IN_BUF_SIZE)
{
qout=0;
}
}
while(ch==0x0A||ch==0xC2);
return(ch);
}
void_putchar(charch)
{
while(flag_tx_ready);
user_tx_buffer=ch;
//invoke_UART_transmit
timer_tx_ctr=3;
bits_left_in_tx=tx_num_of_bits;
internal_tx_buffer=(user_tx_buffer<<1)|0x200;
flag_tx_ready=TRUE;
}
voidflush_input_buffer(void)
{
qin=0;
qout=0;
}
charkbhit(void)
{
return(qin!
=qout);
}
voidturn_rx_on(void)
{
flag_rx_off=FALSE;
}
voidturn_rx_off(void)
{
flag_rx_off=TRUE;
}
串行通信波特率的一种自动检测方法
程序匠人发表于2005-12-1320:
48:
00 阅读全文(2259)|回复(0)|引用通告(0)|编辑
串行通信波特率的一种自动检测方法
(哈尔滨工业大学控制工程系 150001)任贵勇 屈彦成 王常虹
摘 要:
给出了一种利用接收到的字符信息检测串行终端通信波特率的方法。
此方法简单、可靠、易行,并给出了实现这种检测方法的伪代码。
关键词:
自动检测;波特率
串行通信是终端和主机之间的主要通信方式,通信波特率一般选择1800、4800、9600和19200等。
终端的类型有很多种,其通信速率也有很多种选择。
主机怎样确定终端的通信速率呢?
本文给出了一种简单、易行的方法:
设定主机的接收波特率(以9600波特为例),终端发送一个特定的字符(以回车符为例),主机根据接收到的字符信息就可以确定终端的通信波特率。
本文对这种方法予以详述。
1 基本方法
回车符的ASCII值为0x0D。
串行通信时附加一个起始位和终止位,位的传输顺序一般是先传低位再传高位。
此时回车符的二进制表示方式为:
图1 回车符的位序列
串行通信中一个二进制位的传输时间(记为T)取决于通信的波特率,9600波特时一个二进制位的传输时间是19200波特时一个二进制位传输时间的两倍,即:
2*T19200=T9600。
因此,9600波特时一个位的传输时间,19200波特时可以传输两个位。
同样地,9600波特传输两个位的时间在4800波特时只能传送一个位。
主机设定接收波特率为9600,终端只有也以9600波特发送的字符,主机才能正确地接收。
发送波特率高于或低于9600都会使主机接收到的字符发生错误。
接收波特率为9600,终端以不同的波特率发送回车符时,主机接收到的二进制序列如表1所示。
从表1中可以看出,除了19200和1800波特时两种特例情况,其他情形的二进制序列都是9600波特时二进制序列的变换。
取前十个二进制位与9600波特时的二进制位相对应。
忽略缺少停止位‘1’引发的数据帧错误,把接收到的字符表示成字节方式(如表1的最右列所示)。
例如:
在发送速率为1200波特,接收速率为9600波特时,主机得到的字节是0x80,而不是正确的回车符0x0D。
因为在不同的发送速率下(9600,4800,2400,1200)得到的字节不同,所以通过接收字符的判定就可以确定发送波特率。
发送波特率为19200时,其发送速度正好是接收速度(9600波特)的两倍,因此发送端的两个二进制位会被接收端看作一个。
取决于不同的串行接口硬件,‘01’和‘10’这两种二进制位组合可能被认为是‘1’或者‘0’。
幸运的是,只有0~4位存在这样的歧义问题,后面的位因为都是停止位,所以都是‘1’。
因此,发送速率为19200波特时接收到的字符其高半个字节为0xF。
低半个字节可能是多个值中的一个,但不会是0x0,因为0x0D中有相邻的两个‘1’,这就会至少在低半个字节中产生一个‘1’。
因此,整个字节的形式为0xF?
,且低半个字节不为0。
表1 不同波特率下的二进制序列
波特率
接收到的二进制位序列
字节表示
19200
01011000011111111111
0xF?
9600
0 1 0 1 1 0 0 0 0 1
0x0D
4800
00110011110000000011
0xE6
2400
00001111000011111111
0x78
1800
00000x1111x000001111
0xE0
1800
00000x1111x000001111
0xF0
1200
00000000111111110000
0x80
600
00000000000000001111
0x00
300
00000000000000000000
0x00
150
00000000000000000000
0x00
110
00000000000000000000
0x00
发送速率为1800波特时,因为
T1800=T9600*16/3,
而16/3不是整数,接收端二进制位的状态转换时刻和9600波特不一一对应,引起在接收端的一个位接收周期内有状态发生变化的可能。
表1中给出的第六个位(表示为x)就是这种情况。
因为x有可能被看作‘1’,也有可能被看作‘0’,所以发送速率为1800波特时接收到的字节可能是0xE0或者0xF0。
波特率为3600和7200时也有同样的问题,也可以采用同样的方法,但不确定的位数会增加,需要检测的字节种类也会更多。
3600波特和7200波特的传输速率几乎不采用,因此这个问题并不严重。
只要发送波特率在1200~19200之间,我们都可以通过接收到的一个字符对此波特率进行唯一的判定。
2 低波特率的检测
当发送速率低于1200波特时,接收端收到的字节都是0x00,因此只能确定其速率低于1200波特,而不可能再得到更多的信息。
为了解决这个问题,可以在9600波特的速率下继续接收下一个字节信息。
发送速率为600波特或更低时,一个位的发送时间要大于9600波特时整个字节的接收时间。
因此,发送端每一个从‘1’(终止位)到‘0’(起始位)的跳变都会让接收端认为一个新的字节开始了。
表2所示为600波特或更低的传输速率时接收端回车符的二进制序列(只给出开始的一些位)。
表2 低波特率回车符的接收方式
波特率
9600波特二进制序列
时间差
(周期)
时间差
(实时间)
600
160's 161's 160's
32
3.33ms
300
320's 321's 320's
64
6.66ms
150
640's 641's 640's
128
13.33ms
110
870's 871's 870's
174
18.13ms
75
1280's 1281's 1280's
256
26.66ms
50
1920's 1921's 1920's
384
40.00ms
600波特时,第一个从‘1’到‘0’的跳变在初始化以后即刻发生。
这个跳变让接收端得到字节0x00。
第二个跳变在初始化(16+16)*T9600秒以后发生,这会让接收端认为另外一个字节开始接收了。
一个二进制位的接收时间是T9600,所以串行接口电路会在第一个跳变以后10*T9600秒提示第一个字节接收完毕,在(16+16+10)*T9600秒以后提示第二个字节接收完毕。
因此600波特时,第一个字节接收完毕和第二个字节接收完毕的时间差是(16+16+10-10)*T9600=32*T9600秒。
表2的第三列所示是把这个时间差以T9600的个数表示。
因为T9600=1/9600秒=104.16毫秒,相乘可以得到两个字节接收完毕的实时间差。
不同发送波特率的时间差如表2的最后一列所示。
有了这个时间差信息,就可以确定低传输速率时的波特率了:
测定第一个和第二个字节的接收时间差,然后在时间差常数表(表2)里查出哪个波特率下的时间差与之最相近,对应的就是终端发送波特率。
即使测定的时间差有些误差,一般也可以正确地确定波特率。
3 实现方式
通过以上分析,各种波特率都可以通过回车符的发送和接收信息来测定,算法实现的伪代码在本文的最后给出。
应用实践证明了这种方法的有效性。
; Pseudocodetodeterminewhatbaudrateatransmitterisat,
onthebasisofasingle
; RETURN(0x0D)characterreceivedfromit.
Initialisereceivebaudrateto9600
WaitforBytetobereceived
IFByte=0x00THEN
StartTimer
REPEAT
UNTIL(Timer>50msORNewByteReceived)
CASETimerIN
1ms-4ms:
600Baud
5ms-10ms:
300Baud
11ms-15ms:
150Baud
16ms-22ms:
110Baud
23ms-32ms:
75Baud
33ms-49ms:
50Baud
ELSE:
Timedout;reset
ENDCASE;
ELSIFByte>=0xF1THEN
19200Baud
ELSE
CASEByteIN
0x0D:
9600Baud
0xE6:
4800Baud
0x78:
2400Baud
0xE0,0xF0:
1800Baud
0x80:
1200Baud
ELSE:
Linenoise;reset
ENDCASE
ENDIF■
参考文献:
[1]赵依军等.单片微机接口技术[M].北京:
人民邮电出版社,1989.
[2]刘利.软硬件技术参考大全[M].北京:
学苑出版社,1993.
[3]张世一.数字信号处理[M].北京:
北京工业学院出版社,1987.
浅谈单片机程序设计中的“分层思想”(zt)
程序匠人发表于2008-10-2017:
25:
00 阅读全文(6559)|回复(23)|引用通告(0)|编辑
浅谈单片机程序设计中的“分层思想”
随便写下的一点东西,本来打算去发表,不过想想还是算了,不是什么重要的东西,不过这个东西确实很有用。
文章烂的去组织和修改了,随便看看吧。
分层的思想,并不是什么神秘的东西,事实上很多做项目的工程师本身自己也会在用。
看了不少帖子都发现没有提及这个东西,然而分层结构确是很有用的东西,参透后会有一种恍然大悟的感觉。
如果说我不懂LCD怎么驱动,那好办,看一下datasheet,参考一下阿别人的程序,很快就可以做出来。
但是如果不懂程序设计的思想的话,会给你做项目的过程中带来很多很多的困惑。
参考了市面上各种各样的嵌入式书籍,MCS-51,AVR,ARM等都有看过,但是没有发现有哪本是介绍设计思想的,就算有也是凤毛麟角。
写程序不难,但是程序怎么样才能写的好,写的快,那是需要点经验积累的。
结构化模块化的程序设计的思想,使最基本的要求。
然而这么将这个抽象的概念运用到工程实践当中恩?
那需要在做项目的过程中经历磨难,将一些东西总结出来,抽象升华为理论,对经验的积累和技术的传播都大有裨益。
所以在下出来献丑一下,总结一些东西。
就我个人的经验而谈,有两个设计思想是非常重要的。
一个就是“时间片轮的设计思想”,这个对实际中解决多任务问题非常有用,通常可以用这个东西来判断一个人是单片机学习者,还是一个单片机工程师。
这个必须掌握。
由于网上介绍这个的帖子也不少,所以这里就不多说了。
第二个就是我今天想说的主题“分层屏蔽的设计思想”。
下面用扫描键盘程序例子作为引子,引出今天说的东西。
问题的提出
单片机学习板一般为了简单起见,将按键分配的很好,例如整个4*4的键盘矩阵分配到P1口上面,8条控制线,刚好。
这样的话程序也非常好写。
只需要简单的
KEY_DAT=P1;
端口的数据就读进来了。
诚然,现实中没有这么好的事情。
在实际的项目应用当中,单片机引脚的复用相当厉害,这跟那些所谓的单片机学习板就有很大的差别了。
另外一个原因,一般设计来说,是“软件配合硬件”的设计流程,简单点说就是,先确定好硬件原理图,硬件布线,最后才是软件的开发,因为硬件修改起来比较麻烦,相对来说软件修改的时候比较好改。
这个就是中国传统的阴阳平衡哲学原理。
硬件设计和软件设计本来就是鱼和熊掌的关系,两者不可兼得。
方便了硬件设计,很可能给写软件带来很大的麻烦。
反过来说,方便了软件设计,硬件设计也会相当的麻烦。
如果硬
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 通用 IO 模拟 串口 程序