嵌入式STM32--实验四、串口通信实验
原创
©著作权归作者所有:来自51CTO博客作者行走的皮卡丘的原创作品,请联系作者获取转载授权,否则将追究法律责任
嵌入式STM32--实验四、串口通信实验
自律 学习 坚强 ,拒绝迷茫。
作者:行走的皮卡丘
时间:2021/4/4
喜欢就去追,这个红灯等不到,说不定下一个红灯等到了,嘻嘻!!!!!!!
一、 实验目的
- 1、 掌握STM32串口通信原理。
- 2、 学习编程实现STM32的UART通信。
二、 实验设备
硬件:
PC机 一台
STM32开发板 一套
软件:
MDK V4.0 一套
Windows 7 一套
调试助手 一套
三、 实验原理
3.1、状态寄存器USART_SR及函数
第5、6位 RXNE
和TC
-
RXNE
(读数据寄存器非空),当该位被置1的时候,就是提示已经有数据被接收到了,并且可以读出来了。 -
TC
(发送完成),当该位被置位的时候,表示USART_DR
内的数据已经被发送完成了。
在固件库函数里面,读取串口状态的函数是:
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG );
这个函数的第二个入口参数非常关键,它是标示我们要查看串口的哪种状态,比如上面讲解的RXNE
(读数据寄存器非空)以及TC
(发送完成)。
例如:要判断读寄存器是否非空(RXNE
),操作库函数的方法是:
USART_GetFlagStatus(USART1,USART_FLAG_RXNE);
要判断发送是否完成(TC
),操作库函数的方法是:
USART_GetFlagStatus(USART1,USART_FLAG_TC);
3.2、USATR1 发送一个字节
/****************************************************************
USATR1 发送一个字节
功能:发送一个字节的数据
入口参数:发送到数据
出口参数:无
*****************************************************************/
void Send_OneByte(uint8_t onebyte)
{
while(RESET == USART_GetFlagStatus(USART1, USART_FLAG_TC));//等待发送完毕
USART_SendData(USART1, onebyte);
}
3.3、接收一个字节的数据
/****************************************************************
功能:接收一个字节的数据
入口参数:无
出口参数:返回收到的数据
*****************************************************************/
uint8 rxdata;//存储收到的数据
uint8 Receive_OneByte(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE)!= RESET)//串口收到数据
{
rxdata = USART_ReceiveData(USART1);
}
return rxdata;
}
3.4、串口编程为如下步骤:
//1) 串口时钟使能, GPIO 时钟使能
//配置时钟,由于使用串口是在复用IO口,需要打开串口时钟和相应的IO口时钟。
//2) GPIO 端口模式设置
//串口相应的IO口需要配置,输出口配置成复用推挽输出,输出速度根据需要配置,输入口配置为悬空输入。
//3) 串口参数初始化,使能串口。(***\*串口调试助手 与之一致\**** )
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = ***\*115200\****; //波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //1位停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
//收发模式
USART_Init(USART1, &USART_InitStructure ); //初始化串口1
//USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启接收中断。若无中断,该项不需要
USART_Cmd(USART1, ENABLE); //使能串口1
//4) 初始化 NVIC且开启中断(若开启中断才需要该步骤)
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0)
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;
//抢占优先级0,级别可自己设定 ,下面的子优先级可自定
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//5) 编写中断处理函数 (有中断才需要该步骤)
四、实验内容
1、用STM32设计一个与计算机进行串口通讯的实验。STM32向PC发送 123ABC ,用串口调试助手 显示出来,用查询方式完成。
2、用STM32设计一个与计算机进行串口通讯的实验。PC向STM32发送 456789 ,STM32接收后,将 每位数值+3 发送给PC,用 串口调试助手显示出来,用中断方式完成。
3、用STM32设计一个与计算机进行串口通讯的实验,利用PC控制LED的亮灭。
- 1)PC向STM32发送 A ,控制LED1,LED2,LED3同时亮灭,间隔时间0.5S,同时在 串口调试助手显示LED1,LED2,LED3同时亮灭”。
- 2)PC向STM32发送 B ,控制LED1,LED2,LED3正向流水亮灭,间隔时间1S,同时在串口调试助手显示LED1,LED2,LED3正向流水亮灭”。
- 3)PC向STM32发送 C ,控制LED1,LED2,LED3反向流水亮灭,间隔时间2S,同时在 串口调试助手显示LED1,LED2,LED3反向流水亮灭”。
4、利用定时器结合串口实现 电子时钟的功能。功能如下:
- 1)定时器完成 1S 的定时。
- 2)串口调试助手*上显示时间,格式参考如下 12:34:56 ,每秒递增。串口调试助手每2秒刷新一次。
③在LCD上显示时间,格式参考如下 12:34:56 ,每秒递增。例程可参考 FSMC-液晶显示-英文的配套例程。
4.1、用STM32设计一个与计算机进行串口通讯的实验。STM32向PC发送 123ABC,用串口调试助手显示出来,查询方式完成。(代码:实验4-1)
usart.c
函数
#include "sys.h"
#include "usart.h"
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA=0; //接收状态标记
void uart_init(u32 bound){
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
//USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
//USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_Cmd(USART1, ENABLE); //使能串口1
}
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(USART1); //读取接收到的数据
if((USART_RX_STA&0x8000)==0)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了0x0d
{
if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
}
usart.h函数
#ifndef __USART_H
#define __USART_H
#include "stdio.h"
#include "sys.h"
#define USART_REC_LEN 200//定义最大接收字节数 200
#define EN_USART1_RX 1//使能(1)/禁止(0)串口1接收
extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u16 USART_RX_STA; //接收状态标记
//如果想串口中断接收,请不要注释以下宏定义
void uart_init(u32 bound);
#endif
main函数
#include "delay.h"
#include "sys.h"
#include "usart.h"
/**************************************************
* Function main
* @author 行走的皮卡丘
* @date 2021/4/4
* @brief
* @param[in]
* @param[out] void
* @retval void
* @par History 无
* TXD PA.9
* RXD PA.10
*************************************************/
int main(void)
{
u16 t;
u8 data[10]="123ABC";
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
while(1)
{
printf("\r\nSTM32发送的消息为: ");
for(t=0;t<sizeof(data);t++)
{
USART_SendData(USART1, data[t]);//向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
printf("\r\n");//插入换行
delay_ms(1000);
}
}
4.2、用STM32设计一个与计算机进行串口通讯的实验。PC向STM32发送 456789 ,STM32接收后,将 每位数值+3 发送给PC,用 串口调试助手 显示出来,用中断方式完成。(代码:实验4-2)
main函数
#include "sys.h"
#include "usart.h"
/**************************************************
* Function main
* @author 行走的皮卡丘
* @date 2021/4/4
* @brief
* @param[in]
* @param[out] void
* @retval void
* @par History 无
* TXD PA.9
* RXD PA.10
*************************************************/
int main(void)
{
char String[]="0123456789ABCDEFG";
u8 i;
u8 data;
u16 len;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
while(1)
{
if(USART_RX_STA&0x8000)
{
len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
USART_RX_BUF[len] = 0;
printf("\r\n PC发送的消息为:%s\r\n",USART_RX_BUF);
printf(" STM32发送的消息为:");
for(i = 0;i < len;i++)
{
data=String[ USART_RX_BUF[i] - 48 + 3 ];
printf("%c",data);
}
printf("\r\n");//插入换行
USART_RX_STA=0;
}
}
}
4.2.3、用STM32设计一个与计算机进行串口通讯的实验,利用PC控制LED的亮灭。(代码:实验4-3)
1)PC向STM32发送 A ,控制LED1,LED2,LED3同时亮灭,间隔时间0.5S,同时在 串口调试助手显示LED1,LED2,LED3同时亮灭”。
2)PC向STM32发送 B ,控制LED1,LED2,LED3正向流水亮灭,间隔时间1S,同时在 串口调试助手显示LED1,LED2,LED3正向流水亮灭”。
3)PC向STM32发送 C ,控制LED1,LED2,LED3反向流水亮灭,间隔时间2S,同时在 串口调试助手显示LED1,LED2,LED3反向流水亮灭”。
#include "delay.h"
#include "sys.h"
#include "usart.h"
/**************************************************
* Function main
* @author 行走的皮卡丘
* @date 2021/4/4
* @brief
* @param[in]
* @param[out] void
* @retval void
* @par History 无
* TXD PA.9
* RXD PA.10
*************************************************/
#define LED1 PBout(0)
#define LED2 PCout(4)
#define LED3 PCout(3)
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStr;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);//
GPIO_InitStr.GPIO_Mode=GPIO_Mode_Out_PP;//推挽
GPIO_InitStr.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStr.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStr); // PB0 LED1
GPIO_InitStr.GPIO_Pin=GPIO_Pin_4;
GPIO_Init(GPIOC,&GPIO_InitStr); // PC4 LED2
GPIO_InitStr.GPIO_Pin=GPIO_Pin_3;
GPIO_Init(GPIOC,&GPIO_InitStr); // PC3 LED3
GPIO_SetBits(GPIOB,GPIO_Pin_0);
GPIO_SetBits(GPIOC,GPIO_Pin_4);
GPIO_SetBits(GPIOC,GPIO_Pin_3);
}
int main(void)
{
u16 i=0;
u16 len;
u16 data;
LED_Init(); //LED端口初始化
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
while(1)
{
if(USART_RX_STA&0x8000) //接收数据
{
len = USART_RX_STA&0x3f;
for(i = 0;i < len; i++)
{
data = USART_RX_BUF[i];
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
}
USART_RX_STA = 0;
if(data == 'A')
{
data = 0;
printf("LED1,LED2,LED3同时亮灭\r\n");
LED1=1; LED2=1; LED3=1;
while(USART_RX_STA==0)
{
LED1=~LED1; LED2=~LED2; LED3=~LED3;
delay_100ms(5);
}
}
if(data == 'B')
{
data = 0;
printf("LED1,LED2,LED3正向流水亮灭\r\n");
while(USART_RX_STA==0)
{
LED1=1; LED2=1; LED3=0;
delay_100ms(10);
LED1=1; LED2=0; LED3=1;
delay_100ms(10);
LED1=0; LED2=1; LED3=1;
delay_100ms(10);
}
}
if(data == 'C')
{
data = 0;
printf("LED1,LED2,LED3反向流水亮灭\r\n");
LED1=1; LED2=1; LED3=1;
while(USART_RX_STA==0)
{
LED1=0; LED2=1; LED3=1;
delay_100ms(20);
LED1=1; LED2=0; LED3=1;
delay_100ms(20);
LED1=1; LED2=1; LED3=0;
delay_100ms(20);
}
}
}
}
}
4.4、利用定时器结合串口实现 电子时钟的功能。(代码:实验4-4)
- 定时器完成 1S 的定时。
- 形参设置为arr=9999,psc=7199即可
- 串口调试助手上显示时间,格式参考如下 12:34:56 ,每秒递增。串口调试助手每2秒刷新一次。
- 主函数中的死循环,其中flay在定时器2的中断中累加,即一秒以1累加。
- 在LCD上显示时间,格式参考如下 12:34:56,每秒递增。例程可参考FSMC-液晶显示-英文的配套例程。
main函数
#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "timer.h"
#include "lcd.h"
/**************************************************
* Function main
* @author 行走的皮卡丘
* @date 2021/4/4
* @brief
* @param[in]
* @param[out] void
* @retval void
* @par History 无
* TXD PA.9
* RXD PA.10
*************************************************/
int main(void)
{
ILI9341_Init();
ILI9341_GramScan( 6 ); //设置默认扫描方向,其中 6 模式为大部分液晶例程的默认显示方向
LCD_SetColors(GBLUE,BLACK); //设置颜色
ILI9341_Clear(0,0,LCD_X_LENGTH,LCD_Y_LENGTH);
LCD_SetFont(&Font16x24);
LCD_SetTextColor(GREEN); //设置文本颜色
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
LED_Init(); //LED端口初始化
TIM2_Int_Init(9999,7199); //一秒一次中断 72 000 000 /7200=10000 10000/10000=1hz 1/1hz=1s
while(1)
{
if( flay==2 )
{
flay = 0;
printf("%s\r\n",TimeStr);
}
}
}
五、实验总结
加油!!!行走的皮卡丘!!!!