串口是学习单片机重要的一项,用来显示数据和一些简单的控制命令非常方便,经过笔者这几天的测试,总结出了一些需要注意的地方:(以下代码全部基于单片机STM32F407实现)
1、关于发送
除非你勾选了串口调试工具里面的HXE(16进制),否则串口发送的是对应字符的ASCII码,也就是说接收的是每一位都是一个字节的ASCII码。比如说通过串口发送内容:adc123,其实发送的它们对应的ASCII码:61H 62H 63H 31H 32H 33H(H表示16进制)。如果发送勾选了“发送新行”,就会在发送的内容后面添加0DH 0AH(换行符),添加了两个字节。
在单片机编程中,如果用指针定义了一个无变量名的字符串:char *str=”abc123”;系统会自动在该变量后面添加一个‘\0’表示字符串的结束,这样改字符串共占了7个字节。基于这个原因,发送字符串时可以采用下函数实现:
void usartSendString(u8 *str)
{
while(*str != 0)
{
while((USART3->SR&0x40)==0); //等待发送缓冲器空
USART_SendData(USART3,*str); //发送字符
str++;
}
而不是采用大多数采用的函数:
void uart3SendChars(u8 *str, u16 strlen)
{
u16 k= 0 ;
do {uart3SendChar(*(str + k)); k++; }
while (k < strlen);
}
比较以上两个函数,可以发现第一个函数入口参数少了要发送字符串的字长,第一个函数可以实现自动计算字长。当然,第一个函数仅能实现对字符串的发送,并不能实现对数组的发送。
2、关于接收
相信大多数人都曾想实现将串口接收的内容在LCD上显示,一个非常简便的办法就是在串口接收中断中直接将接收到的字符显示在LCD上,但是由于刷新LCD需要较长的时间,在发送了一长串字符串后,很容易由于刷新LCD需要较长的时间而导致接收到的内容不全。笔者想出了一个比较巧妙的函数,可以解决这样的烦恼:
#define USART3_REC_NUM 100 //定义最大接收字节数
u8 receive_str[USART3_REC_NUM] = {0}; //接收缓存数组
u16 uart_byte_count=0; //接收的字节数
//串口中断函数
void USART3_IRQHandler(void)
{
u8 rec_data;
if(USART_GetITStatus(USART3,USART_IT_RXNE) != RESET)
{
rec_data=(u8)USART_ReceiveData(USART3); //接收内容
receive_str[uart_byte_count]=rec_data;//将接收到的内容存放在数组中
uart_byte_count++; //接收的字节数+1
}
}
//将串口接收到的数据拷贝出来,拷贝出来的字符串末尾保留了换行符'\x0d' '\x0a'
u8 USART_Receive2Str(u8 *str)
{
u16 i;
if(receive_str[uart_byte_count-1]== 0x0a) //接收到换行符
{
for(i=0;i<uart_byte_count;i++)
{
*(str+i) =receive_str; //将数据拷贝出来
receive_str=0; //将接收缓冲数组清零
}
uart_byte_count=0;
return 1; //成功拷贝到数据
}
else return 0; //拷贝数据失败
}
这样就将数组中的内容拷贝到其他数组中,进行相应的处理。但是要特别注意的是,要想使用以上函数,必须在串口调试工具中勾选“发送新行”才能实现,并且拷贝出来的字符串末尾保留了换行符'\x0d' '\x0a'。要想实现控制,可以使用下列方式:
u8 str[20]; //最多能拷贝出来20个字符
if(USART_Receive2Str(str) == 1)
{
if(strcmp("Light_led1\x0d\x0a",(char*)str)==0) LED1=0;//点亮LED1
elseif(strcmp("Close_led1\x0d\x0a",(char *)str)==0) LED1=1;//关闭LED1
elseif(strcmp("Open_beep\x0d\x0a ",(char *)str)==0)BEEP=1; //打开蜂鸣器
elseif(strcmp("Close_beep\x0d\x0a ",(char *)str)==0)BEEP=0;//关闭蜂鸣器
//使用完后一定要将数组清零,方便下一次使用
u8 i=0;
while(*(str+i) !=0) /
{
*(str+i) = 0;
i++;
}
i=0;
}
以下代码实现通过开发板STM32F407实现对WIFI模块8266的控制,每按一次按键就发送一条指令,并将模块返回的信息显示到LCD上:
//控制指令,末尾的\x0d\x0a一定要加上,否则8266不能识别指令,相当于勾选了“发送新行”
char *Ins1 = "AT+RST\x0d\x0a";
char *Ins2 = "AT+CWMODE=1\x0d\x0a";
char *Ins3 = "AT+CWLAP\x0d\x0a";
char *Ins4 ="AT+CWJAP=\"DLUTv0\",\"bugaosuniaaa\"\x0d\x0a";
char *Ins5 ="AT+CIPSTART=\"TCP\",\"192.168.5.98\",8080\x0d\x0a";
if(get_key_msg(&keymsg) == 1) //获得按键消息
{
static u8 i=0;
if((keymsg.key == KEY0)&& (keymsg.status == KEY_DOWN))
{
switch(i)
{
case 0:uart3SendString((u8*)Ins1);i=1;LCD_DisplayString(10,10,16,"SendIns1");break;
case 1:uart3SendString((u8*)Ins2);i=2;LCD_DisplayString(10,10,16,"SendIns2");break;
case 2:uart3SendString((u8*)Ins3);i=3;LCD_DisplayString(10,10,16,"SendIns3");break;
case 3:uart3SendString((u8*)Ins4);i=4;LCD_DisplayString(10,10,16,"SendIns4");break;
case 4:uart3SendString((u8*)Ins5);i=0;LCD_DisplayString(10,10,16,"SendFinished");break;
default: break;
}
}
}
if(USART_Receive2Str(string) ==1)//将模块返回的内容显示在LCD上
{
u8 i=0;
LCD_DisplayString(1,yc,12,string);
yc+=12;
if(yc>=320){LCD_Clear(WHITE);yc=50;}
while(*(str+i) !=0)
{
*(str+i) = 0;
i++;
}
i=0;
}