实例
通过配置串口,将电位器采集到的电压通过串口发送到上位机,将上位机发送的数据显示到下位机。
电路分析
电路上有两个串口,串口 1(PA9,PA10)通过 RS232 接口链接,串口 2(PA2,PA3)通过 USB 接口链接,我们在调试过程中使用的是 USB 连接方式,故我们通常配置串口 2m,直接使用下载线进行串口通讯,不配置串口 1。
工程建立
本次工程在上一次的工程上继续修改,复制上一次的工程作为本次的工程基础。
添加库函数
本例中使用到了串口通讯功能,故要添加以下库函数:
初始化串口 2
将 PA2 和 PA3 分别设置为 TX 和 RX 对应的模式,开启串口发送和接收模式,并配置接收中断:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| void USART2_Init(u32 baudrate) { NVIC_InitTypeDef NVIC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitSturcture; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); NVIC_InitStructure.NVIC_IRQChannel=USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0; NVIC_InitStructure.NVIC_IRQChannelSubPriority=0; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA,&GPIO_InitStructure); USART_InitSturcture.USART_BaudRate=baudrate; USART_InitSturcture.USART_WordLength=USART_WordLength_8b; USART_InitSturcture.USART_StopBits=USART_StopBits_1; USART_InitSturcture.USART_Parity=USART_Parity_No; USART_InitSturcture.USART_Mode=USART_Mode_Rx|USART_Mode_Tx; USART_InitSturcture.USART_HardwareFlowControl=USART_HardwareFlowControl_None; USART_Init(USART2,&USART_InitSturcture); USART_Cmd(USART2,ENABLE); USART_ITConfig(USART2,USART_IT_RXNE,ENABLE); }
|
发送程序
通过串口 2 将字符串发送到上位机:
1 2 3 4 5 6 7 8 9 10 11
| void USART2_SendString(u8 *str) { u8 index=0; do { USART_SendData(USART2,str[index]); while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)==0); index++; } while(str[index]!=0); }
|
发送字符串函数的传入参数为一个指针,在这里填一个数组,即为将数组内的数据逐个逐个发送出去,直到发送完毕,void USART_SendData (USART_TypeDef* USARTx, uint16_t Data); 可以发送一个字节的数据。
接收程序
通过串口 2 接收上位机下发的数据:
定义全局变量如下:
1 2 3
| u8 RX_Buffer[20]; u8 RX_Over=0; u8 RX_Cnt=0;
|
约定每次接收到回车符 (\r\n) 即为一次数据传输完成,则修改接收中断:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| void USART2_IRQHandler(void) { u8 temp; if(USART_GetITStatus(USART2,USART_IT_RXNE)==SET) { USART_ClearITPendingBit(USART2,USART_IT_RXNE); temp=USART_ReceiveData(USART2); if(temp=='\n') { RX_Cnt=0; RX_Over=1; } else { RX_Buffer[RX_Cnt]=temp; RX_Cnt++; } } }
|
回传和显示数据
实例,给程序添加如下功能:
- 在 LCD 的第四行显示提示文本:“USART Received:”
- 在 LCD 的第五行显示接收到的数据,上电时显示:“None.”,字体颜色设置为红色;
- 设定串口波特率为 115200,并每隔 1 秒通过串口 2 向 PC 发送当前采集到的电压,格式为 “ADC: X.XXV”,每次发送结束均要换行。
定义全局变量:
1 2
| u8 USART_Send_Flag=0; //发送时间到达标志位 u8 Send_String[20]; //发送用的字符串
|
修改定时器中断程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| void TIM1_UP_IRQHandler(void) { static u16 cnt=0,adc_cnt=0,usart_cnt=0; if(TIM_GetITStatus(TIM1,TIM_IT_Update)==SET) //1ms到 { TIM_ClearITPendingBit(TIM1,TIM_IT_Update); if(++cnt>=50) { cnt=0; Flag50ms=1; if(++adc_cnt>=10) { adc_cnt=0; ADC_Flag=1; if(++usart_cnt>=2) { usart_cnt=0;//清空USART计时 USART_Send_Flag=1;//1s到 } } } } }
|
修改主程序如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| int main(void) { float ADC_Value=0;
SysTick_Config(SystemCoreClock/1000); Delay_Ms(200);
STM3210B_LCD_Init(); LCD_Clear(Black); LCD_SetBackColor(Black); Timer1_Init(); LED_Init(); Key_Init(); ADC1_Init();
USART2_Init(115200);
LCD_SetTextColor(White); LCD_DisplayStringLine(Line1,(u8*)" Welcome!"); LCD_DisplayStringLine(Line4,(u8*)" USART Received:"); LCD_DisplayStringLine(Line5,(u8*)" None."); while(1) { if(Flag50ms) { Flag50ms=0; Key_Scan(); } sprintf((char*)Display_String," LED1-I/O:%d",L1_Status); LCD_SetTextColor(Blue); LCD_DisplayStringLine(Line2,Display_String);
if(ADC_Flag) { ADC_Flag=0; ADC_Value=ADC_Get(8)*3.3/4096; sprintf((char*)Display_String," ADC:%.2fV",ADC_Value); LCD_SetTextColor(Green); LCD_DisplayStringLine(Line3,Display_String); }
if(USART_Send_Flag) { USART_Send_Flag=0; sprintf((char*)Send_String,"ADC:%.2fV\r\n",ADC_Value); USART2_SendString(Send_String); }
if(RX_Over) { RX_Over=0; LCD_DisplayStringLine(Line5,RX_Buffer); } } }
|
下载测试
使用串口助手软件进行调试,设置好对应的参数,然后打开串口,可以看到接收区能够顺利接收到采集到的电压数据,通过串口下发数据,开发板能够正常显示接收到的数据。
优化改进
下载后发现在接收到的数据显示上,多了一个未能够正确识别的字符,其实这个字符就是之前约定的结束标志换行符,若要取消换行符的显示,可以对串口接收程序做如下修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| void USART2_IRQHandler(void) { u8 temp; if(USART_GetITStatus(USART2,USART_IT_RXNE)==SET) { USART_ClearITPendingBit(USART2,USART_IT_RXNE); temp=USART_ReceiveData(USART2); if(temp=='\n') { //检测到传输完成后,将回车换行符的'\r'替换为'\0' RX_Buffer[RX_Cnt-1]='\0'; RX_Cnt=0; RX_Over=1; } else { RX_Buffer[RX_Cnt]=temp; RX_Cnt++; } } }
|
改进后能够正常显示:
![](https://i.loli.net/2021/01/19/eNxB7VarmTtfHdi.png)
为什么做如上修改可以使后续的字符不予显示?
1 2 3 4 5 6 7 8 9 10 11 12 13
| void LCD_DisplayStringLine(u8 Line, u8 \*ptr) { u32 i = 0; u16 refcolumn = 319;//319;
while ((\*ptr != 0) && (i < 20)) // 20 { LCD_DisplayChar(Line, refcolumn, *ptr); refcolumn -= 16; ptr++; i++; } }
|
在调用程序 LCD_DisplayStringLine 对字符串进行显示时,其显示程序也是做的单字节发送显示,当指针为 0 或者字符串长度超出 20 时,停止显示,故将回车换行的 '\r' 改为 '\0' 即可隐藏后续内容。