本帖最后由 gdmgb520 于 2011-5-17 16:27 编辑
串口是很重要也很基础的东西,以前做实验的时候也做过,但是现在看来,那时候根本就没有彻底弄懂。这几天做一个与串口有关的东西,仔细做过了之后才算把他弄明白。现在跟大家分享一下我的心得。
我做的是PC与单片机通信的,PC 发送的数据格式是 包头(4B)+通道号(1B)+状态(1B)+时间(系统6B、任务6B)+包尾(1B)(共19字节):ff ff ff ff 01 0f 09 09 09 23 59 59 08 08 08 08 08 08 fb 。串口工作状态是:9600波特率,校验位:None,数据位:8,停止位:1。
要求是:先要进行包头的校验,然后进行通道号的校验(因为PC串口接了好几个设备),然后才开始接收数据。
首先,我弄明白的是串口的初始化,虽然这个很简单,但是一开始我就是没弄对。而且我当时看了网上的一些例子,照着那些例子写的(看例子之前我还看过教材),结果数据就是有乱码,后面晚上又把教材仔仔细细看了一遍才弄对。我用的是串口工作方式1,板子的晶振是11.0592,所以串口的初始化应该如下:
/*----------串口初始化-----------*/
void Init_Com(void)
{
SCON = 0x50; //串口工作在方式1;允许接收
PCON = 0x00; // 波特率不*2
TMOD = 0x20; //定时器1工作在方式2,自动重装
TH1 = 0xFD; // 波特率为9600
TR1 = 1; // 定时器1启动计数
ES = 1; // 串口开中断
PS = 1; // 串口高优先级
EA = 1; // 开CPU中断
}
由于我这里有特殊要求,所以串口设了高优先级PS=1。有时候你会看到有的初始化中没有EA和ES,而是EI=0x09,实际上这是一回事,EA和ES就是EI的第七位和第四位。还有的人给TL1也装入了0XFD(TL1=0XFD),这实际上是没有必要的,因为TMOD = 0x20; 定时器1工作在方式2,自动重装,即第八位做计数器并计数满以后自动把高八位的数据装入第八位。此时的PCON一定是等于0x00的,不然波特率就变成了19200,网上有的例子就是错的。
然后,串口的收发其实还是很简单的。
串口接收,实际上就是等待串口接收中断。每当MCU的串口接收到一个字节数据后就会触发串口接收中断,所以我们只要在中断程序中把串口接收寄存器中的数据读入变量即可(ch=SBUF;)。而串口的发送其实就是把变量中的数据送入串口发送寄存器。(这两个寄存器物理上是不一样的,但共用一个地址,我们在操作中完全可以把他们当成是一个寄存器。)下面是串口接收一个字符和发送一个字符的子函数:
void Serial_Com() interrupt 4 //串口接收中断
{
if (RI)
{
RI=0; //串口接收中断标志必须是有软件清零的,且应该在中断内部清零
cmd=SBUF;
read_flag=1;
}
}
发送一个字符子函数:
void Send_Char_Com(void)
{
SBUF=cmd;
while(TI==0); //gb 在串口接收中断里如果不加if(RI){}语句,那么这里的while(TI);语句就必须有,否则PC串口会一直接收到信息。这也说明:在单纯的串口接收程序中必须有if(RI){}语句
TI=0; //gb 发生程序中的while(TI)和接收程序中的if(RI)两者都没有时串口收发不正常,一直接收到数据;两者有一个或都存在时串口工作正常。
}
说明一下:这两个子程序中的if(RI==0)和while(TI==0)为什么要加我也不清楚,但事实证明要加,还望老鸟指点!!!!!
然后在主函数中做一个循环,一直调用就可以了。这样就实现了最基本的串口收发(一个字节)。
/*---------------------------2011.05.17---------------------------------------*/
今天重新读自己的帖子,补充下现在对RI和TI的理解。方便新人,呵呵,也保证完整性。嘿嘿
RI和TI是接收和发送中断标志,即表示接收完一个字节或发送完一个字节,该标志应该是需要软件清零(硬件不自动清零)。所以,如果进入中断后软件不清零该标志,那么单片机会再次进入中断。(大概应该是这样,没有看51的书籍,应该参考书籍。)
一句话,就是这两个标志需要软件清零。
先休息一下,保存一下。
先保存一下! |