嵌入式STM32--实验四、串口通信实验_串口通信

嵌入式STM32--实验四、串口通信实验

自律 学习 坚强 ,拒绝迷茫。

作者:行走的皮卡丘

时间:2021/4/4

喜欢就去追,这个红灯等不到,说不定下一个红灯等到了,嘻嘻!!!!!!!


一、 实验目的

  • 1、 掌握STM32串口通信原理。
  • 2、 学习编程实现STM32的UART通信。

二、 实验设备

硬件

PC机 一台

STM32开发板 一套

软件

MDK V4.0 一套

Windows 7 一套

调试助手 一套

三、 实验原理

3.1、状态寄存器USART_SR及函数

嵌入式STM32--实验四、串口通信实验_单片机_02

第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)

嵌入式STM32--实验四、串口通信实验_单片机_03

​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)

嵌入式STM32--实验四、串口通信实验_串口通信_04

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反向流水亮灭”。

嵌入式STM32--实验四、串口通信实验_串口_05

#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即可

嵌入式STM32--实验四、串口通信实验_串口_06

  • 串口调试助手上显示时间,格式参考如下 12:34:56 ,每秒递增。串口调试助手每2秒刷新一次。
  • 主函数中的死循环,其中flay在定时器2的中断中累加,即一秒以1累加。

嵌入式STM32--实验四、串口通信实验_嵌入式_07

  • 在LCD上显示时间,格式参考如下 12:34:56,每秒递增。例程可参考FSMC-液晶显示-英文的配套例程。

嵌入式STM32--实验四、串口通信实验_串口通信_08

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);
}

}
}

五、实验总结

加油!!!行走的皮卡丘!!!!