嵌入式开发十八:USART串口通信实验
上一节我们学习了串口通信的基本理论,串口通信是学习单片机的一个重要的一步,非常重要,这一节我们通过实验来学习串口通信的使用,以及串口的接收中断的使用。
一、发送单个字节uint8_t数据或者字符型数据
实现的功能:
STM32F4 通过串口和上位机通信,发送单个字节数据(0-255)或者字符给上位机,然后显示在电脑串口助手上。
my_usart.h文件内容
#ifndef __MY_USART1_H__
#define __MY_USART1_H__
#include <stdint.h>void My_UsartInit(void); //串口初始化
void Usart_SendByte(uint8_t date); //发送一字节函数#endif
my_usart.c文件内容
#include "stm32f4xx.h"
#include "myusart.h"
#include<stdio.h> //对printf()函数进行重定向引入头文件/******************************第一步:串口时钟使能,GPIO 时钟使能。第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数第三步:GPIO 初始化设置:要设置模式为复用功能第四步:串口初始化:设置波特率,字长,奇偶校验等参数。第五步:使能串口*/void My_UsartInit(void)
{//1.第一步:串口时钟使能,GPIO 时钟使能RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//2.第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);//3.第三步:GPIO 初始化设置:要设置模式为复用功能GPIO_InitTypeDef Struct1;Struct1.GPIO_Mode=GPIO_Mode_AF;Struct1.GPIO_Pin=GPIO_Pin_9 |GPIO_Pin_10;Struct1.GPIO_Speed=GPIO_Speed_100MHz;GPIO_Init(GPIOA,&Struct1);//4.第四步:串口初始化:设置波特率,字长,奇偶校验等参数。USART_InitTypeDef Struct2;Struct2.USART_BaudRate=115200; //设置波特率Struct2.USART_WordLength=USART_WordLength_8b; //数据位8位Struct2.USART_Parity=USART_Parity_No; //无校验位Struct2.USART_StopBits=USART_StopBits_1; //1位停止位Struct2.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不需要硬件流控Struct2.USART_Mode= USART_Mode_Rx |USART_Mode_Tx; //设置usart1既可以接收也可以发送USART_Init(USART1,&Struct2);//5.第五步:使能串口USART_Cmd(USART1,ENABLE);}//发送一字节(一个字符)函数
void Usart_SendByte(uint8_t data)
{USART_SendData(USART1,data); //这是一个库函数,用于将一个字节的数据写入USART的数据寄存器 while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕}//对printf()函数进行重定向
int fputc(int ch, FILE *f)
{USART_SendData(USART1,ch); //通过串口发送数据,每次发送一个字符while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕return ch;
}
main.c代码
#include <stdio.h>
#include "myusart.h"int main(void)
{My_UsartInit();printf("发送一个字节:\n");Usart_SendByte('a');printf("\n");Usart_SendByte(97);printf("\n");while(1){}}
关键部分解读:
发送过程:调用串口发送函数USART_SendData(),但是需要保证在发送下一个字节之前,必须确保当前字节已被成功发送。否则可能会导致新数据写入时覆盖未发送完的数据,造成通信错误。因此,内部使用死循环来控制,调用库函数USART_GetFlagStatus()检查标志位来判断发送的状态!
- 这个
while循环的作用是等待USART的数据寄存器空标志(TXE)被置位。当发送数据寄存器(TDR)中有数据时,TXE标志为RESET。只有当TDR中的数据已被移到移位寄存器中,TXE标志才会被置位。当TXE标志为SET时,表示发送数据寄存器已空,可以发送下一个字节- 具体来说,发送数据过程包括:
- 将数据写入TDR。
- 数据从TDR移到移位寄存器。
- 当移位寄存器开始发送数据时,TDR变空,TXE标志被置位。
while循环确保在TDR变空之前不会发送新的数据,从而避免数据丢失或覆盖。

二、发送一个16位的数据uint16_t
实现的功能:
STM32F4 通过串口和上位机通信,发送两个字节数据(16位)给上位机,然后显示在电脑串口助手上。我们知道串口通信一次只能发送8位的数据,那么如何实现一次发16位呢?
my_usart.h文件内容
#ifndef __MY_USART1_H__
#define __MY_USART1_H__
#include <stdint.h>void My_UsartInit(void); //串口初始化
void USART_SendHalfWord(uint16_t data); //发送两个字节函数#endif
my_usart.c
#include "stm32f4xx.h"
#include "myusart.h"
#include <stdio.h> //对printf()函数进行重定向引入头文件/******************************第一步:串口时钟使能,GPIO 时钟使能。第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数第三步:GPIO 初始化设置:要设置模式为复用功能第四步:串口初始化:设置波特率,字长,奇偶校验等参数。第五步:使能串口*/void My_UsartInit(void)
{//1.第一步:串口时钟使能,GPIO 时钟使能RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//2.第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);//3.第三步:GPIO 初始化设置:要设置模式为复用功能GPIO_InitTypeDef Struct1;Struct1.GPIO_Mode=GPIO_Mode_AF;Struct1.GPIO_Pin=GPIO_Pin_9 |GPIO_Pin_10;Struct1.GPIO_Speed=GPIO_Speed_100MHz;GPIO_Init(GPIOA,&Struct1);//4.第四步:串口初始化:设置波特率,字长,奇偶校验等参数。USART_InitTypeDef Struct2;Struct2.USART_BaudRate=115200; //设置波特率Struct2.USART_WordLength=USART_WordLength_8b; //数据位8位Struct2.USART_Parity=USART_Parity_No; //无校验位Struct2.USART_StopBits=USART_StopBits_1; //1位停止位Struct2.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不需要硬件流控Struct2.USART_Mode= USART_Mode_Rx |USART_Mode_Tx; //设置usart1既可以接收也可以发送USART_Init(USART1,&Struct2);//5.第五步:使能串口USART_Cmd(USART1,ENABLE);}//发送两个字节(16位)数据函数
void USART_SendHalfWord(uint16_t data)
{// 分离高8位和低8位uint8_t tmp_h = data >>0x08;//将date右移8位,取得高8位数据并赋值给tmp_h。右移8位相当于将高8位移到低8位的位置,高8位的原位置被0填充。uint8_t tmp_l = data & 0xFF;//将date和0xff(255,二进制为11111111)高八位为0,进行按位与运算,取得低8位数据并赋值给tmp_l。按位与运算将高8位清零,仅保留低8位。// 发送高8位USART_SendData(USART1, tmp_h); while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);// 发送低8位USART_SendData(USART1, tmp_l);while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);}//对printf()函数进行重定向
int fputc(int ch, FILE *f)
{USART_SendData(USART1,ch); //通过串口发送数据,每次发送一个字符while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕return ch;
}
main.c
#include <stdio.h>
#include "myusart.h"int main(void)
{My_UsartInit();//发送两个字节的数据USART_SendHalfWord(0xffee);while(1){}}

关键部分解读:
串行通信接口通常一次只能处理8位数据,那么,我们就可以通过位运算拿到数据的高八位和低八位分别发送,
Usart_SendHalfWord函数将16位数据分成两个8位数据(高8位和低8位),然后分别通过Usart_SendByte函数发送出去。这种方式在串行通信中很常见。使用示例
假设我们有一个要发送的16位数据
0x1234:
tmp_h = 0x12:0x1234右移8位得到高8位0x12。tmp_l = 0x34:0x1234和0xff按位与得到低8位0x34。- 调用
Usart_SendByte(pUSARTx, 0x12)发送高8位。- 调用
Usart_SendByte(pUSARTx, 0x34)发送低8位。这确保16位数据能够通过支持8位传输的USART接口完整发送。
三、发送8位的数组(uint8_t)
实现的功能:
STM32F4 通过串口和上位机通信,发送一个数组(每个元素都是uint8_t类型)或者字符数组给上位机,然后显示在电脑串口助手上。
my_usart.h
#ifndef __MY_USART1_H__
#define __MY_USART1_H__
#include <stdint.h>void My_UsartInit(void); //串口初始化
void Usart_SendByte(uint8_t date); //发送一字节函数
void USART1_SendArray(uint8_t *array, uint16_t length);//发送一个数组
#endif
my_usart.c
#include "stm32f4xx.h"
#include "myusart.h"
#include<stdio.h> //对printf()函数进行重定向引入头文件/******************************第一步:串口时钟使能,GPIO 时钟使能。第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数第三步:GPIO 初始化设置:要设置模式为复用功能第四步:串口初始化:设置波特率,字长,奇偶校验等参数。第五步:使能串口*/void My_UsartInit(void)
{//1.第一步:串口时钟使能,GPIO 时钟使能RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//2.第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);//3.第三步:GPIO 初始化设置:要设置模式为复用功能GPIO_InitTypeDef Struct1;Struct1.GPIO_Mode=GPIO_Mode_AF;Struct1.GPIO_Pin=GPIO_Pin_9 |GPIO_Pin_10;Struct1.GPIO_Speed=GPIO_Speed_100MHz;GPIO_Init(GPIOA,&Struct1);//4.第四步:串口初始化:设置波特率,字长,奇偶校验等参数。USART_InitTypeDef Struct2;Struct2.USART_BaudRate=115200; //设置波特率Struct2.USART_WordLength=USART_WordLength_8b; //数据位8位Struct2.USART_Parity=USART_Parity_No; //无校验位Struct2.USART_StopBits=USART_StopBits_1; //1位停止位Struct2.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不需要硬件流控Struct2.USART_Mode= USART_Mode_Rx |USART_Mode_Tx; //设置usart1既可以接收也可以发送USART_Init(USART1,&Struct2);//5.第五步:使能串口USART_Cmd(USART1,ENABLE);}//发送一字节函数
void Usart_SendByte(uint8_t data)
{USART_SendData(USART1,data); while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕}//发送一个数组
void USART1_SendArray(uint8_t *array, uint16_t len)
{for (uint16_t i = 0; i < len; i++){Usart_SendByte(array[i]);}
}//对printf()函数进行重定向
int fputc(int ch, FILE *f)
{USART_SendData(USART1,ch); //通过串口发送数据,每次发送一个字符while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕return ch;
}
main.c
#include <stdio.h>
#include "myusart.h"int main(void)
{My_UsartInit();// 要发送的字节数组uint8_t dataArray1[] = {1,2,3,4,5,6,7,8,9,10};int len =sizeof(dataArray1) / sizeof(dataArray1[0]);// 发送一个数组 USART1_SendArray(dataArray1, len);while (1){}}

四、发送字符串数据
实现的功能:
STM32F4 通过串口和上位机通信,发送字符串给电脑,然后显示在电脑串口助手上。
my_usart.h文件内容
#ifndef __MY_USART1_H__
#define __MY_USART1_H__
#include <stdint.h>void My_UsartInit(void); //串口初始化
void USART1_SendString(char *str); //发送一个字符串
#endif
my_usart.c文件内容
#include "stm32f4xx.h"
#include "myusart.h"
#include<stdio.h> //对printf()函数进行重定向引入头文件/******************************第一步:串口时钟使能,GPIO 时钟使能。第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数第三步:GPIO 初始化设置:要设置模式为复用功能第四步:串口初始化:设置波特率,字长,奇偶校验等参数。第五步:使能串口*/void My_UsartInit(void)
{//1.第一步:串口时钟使能,GPIO 时钟使能RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//2.第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);//3.第三步:GPIO 初始化设置:要设置模式为复用功能GPIO_InitTypeDef Struct1;Struct1.GPIO_Mode=GPIO_Mode_AF;Struct1.GPIO_Pin=GPIO_Pin_9 |GPIO_Pin_10;Struct1.GPIO_Speed=GPIO_Speed_100MHz;GPIO_Init(GPIOA,&Struct1);//4.第四步:串口初始化:设置波特率,字长,奇偶校验等参数。USART_InitTypeDef Struct2;Struct2.USART_BaudRate=115200; //设置波特率Struct2.USART_WordLength=USART_WordLength_8b; //数据位8位Struct2.USART_Parity=USART_Parity_No; //无校验位Struct2.USART_StopBits=USART_StopBits_1; //1位停止位Struct2.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不需要硬件流控Struct2.USART_Mode= USART_Mode_Rx |USART_Mode_Tx; //设置usart1既可以接收也可以发送USART_Init(USART1,&Struct2);//5.第五步:使能串口USART_Cmd(USART1,ENABLE);}//发送一个字符串
void USART1_SendString(char *str)
{while (*str!='\0'){USART_SendData(USART1,*str);while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕str++;}
}//对printf()函数进行重定向
int fputc(int ch, FILE *f)
{USART_SendData(USART1,ch); //通过串口发送数据,每次发送一个字符while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕return ch;
}
main.c内容:
#include <stdio.h>
#include "myusart.h"int main(void)
{My_UsartInit();// 要发送的字符串char *dataString = "Hello World!";// 发送字符串printf("发送一个字符串:\n");USART1_SendString(dataString);printf("\n");while (1){ }}

五、单片机接收电脑发送的数据显示在串口助手上(接收中断)
使用串口接收中断可以让CPU在没有数据到达时执行其他任务,而不需要浪费时间轮询接收寄存器。这提高了CPU利用率和系统效率,检查标志位判断是否发生接收中断(USART_IT_RXNE),如果是,读取接收到的数据,然后通过清除接收中断标志以准备接收下一个字节。
实现的功能:
将接收数据设置为接收中断,当上位机发送数据到STM32F407的USART1时,STM32会接收这个数据并通过中断处理函数将数据发送回上位机。上位机的串口助手会显示发送和接收的数据。
my_usart.h
#ifndef __MY_USART1_H__
#define __MY_USART1_H__
#include <stdint.h>void USART1_Config(void); //串口配置
void USART1_SendByte(uint8_t data); //发送一个字节函数
void USART1_IRQHandler(void); //中断服务函数
#endif
my_usart.c
#include "stm32f4xx.h"
#include "myusart.h"
#include<stdio.h> //对printf()函数进行重定向引入头文件/******************************第一步:串口时钟使能,GPIO 时钟使能。第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数。第三步:GPIO 初始化设置:要设置模式为复用功能。第四步:串口初始化:设置波特率,字长,奇偶校验等参数。第五步:开启中断并且初始化 NVIC,使能中断(如果需要开启中断才需要这个步骤)。第六步:使能串口。第七步:编写中断处理函数:函数名格式为 USARTxIRQHandler(x 对应串口号)。*/void USART1_Config(void)
{//第一步:串口时钟使能,GPIO 时钟使能RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);//第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数。GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);// 第三步:GPIO 初始化设置:要设置模式为复用功能。GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(GPIOA, &GPIO_InitStructure);// 第四步:串口初始化:设置波特率,字长,奇偶校验等参数。USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 115200;USART_InitStructure.USART_WordLength = USART_WordLength_8b;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_Tx | USART_Mode_Rx;USART_Init(USART1, &USART_InitStructure);// 第五步:开启中断并且初始化 NVIC,使能USART1接收中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);// 第六步:使能串口USART_Cmd(USART1, ENABLE);}//发送一字节函数
void USART1_SendByte(uint8_t data)
{USART_SendData(USART1,data); while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕}// 第七步:编写中断处理函数:函数名格式为 USARTxIRQHandler(x 对应串口号)
void USART1_IRQHandler(void)
{if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){// 读取接收到的数据uint8_t receivedData = USART_ReceiveData(USART1);// 将接收到的数据发送回电脑USART1_SendByte(receivedData);// 清除中断标志USART_ClearITPendingBit(USART1, USART_IT_RXNE);}
}//对printf()函数进行重定向
int fputc(int ch, FILE *f)
{USART_SendData(USART1,ch); //通过串口发送数据,每次发送一个字符while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕return ch;
}
main.c
#include "myusart.h"int main(void)
{// 配置USART1USART1_Config();while (1){// 主循环中无需处理接收数据,接收数据在中断中处理}
}

六、向单片机发送指令点亮LED
实现的功能:
STM32F4 通过串口和上位机通信,发送字符串(指令): open(通过串口助手),然后单片机接收,点亮LED灯;
本实验主要是串口通信的控制功能,演示串口通信可以增强硬件的能力!
my_led..h
#ifndef __MYLED_H
#define __MYLED_Hvoid LED_Init(void);#endif
my_led.c
#include "stm32f4xx.h" // Device header
#include "myled.h"/*开时钟 打开外设对应的时钟(查看参考手册,该外设挂在哪个数据总线上),对应GPIO在哪条总线开哪条GPIOF外设 挂在AHB1总线上,所以要打开AHB1的时钟,双击函数,右键->go to definition*/void LED_Init(void)
{//第一步:使能GPIOF的时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能 GPIOF 时钟//第二步:GPIOF9,F10 初始化设置GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;//LED0 和 LED1 对应 IO 口GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHzGPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化 GPIO//第三步:设置灯的初始状态GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);//GPIOF9,F10 设置高电平,灯灭
}
my_usart.h
#ifndef __MY_USART1_H__
#define __MY_USART1_H__
#include <stdint.h>void USART1_Config(void); //串口配置
void USART1_IRQHandler(void);
#endif
my_usart.c
#include "stm32f4xx.h"
#include "myusart.h"
#include "myled.h"
#include "string.h"
#include <stdio.h> /******************************第一步:串口时钟使能,GPIO 时钟使能。第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数。第三步:GPIO 初始化设置:要设置模式为复用功能。第四步:串口初始化:设置波特率,字长,奇偶校验等参数。第五步:开启中断并且初始化 NVIC,使能中断(如果需要开启中断才需要这个步骤)。第六步:使能串口。第七步:编写中断处理函数:函数名格式为 USARTxIRQHandler(x 对应串口号)。*********************************/void USART1_Config(void)
{//第一步:串口时钟使能,GPIO 时钟使能RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);//第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数。GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);// 第三步:GPIO 初始化设置:要设置模式为复用功能。GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(GPIOA, &GPIO_InitStructure);// 第四步:串口初始化:设置波特率,字长,奇偶校验等参数。USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 115200;USART_InitStructure.USART_WordLength = USART_WordLength_8b;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_Tx | USART_Mode_Rx;USART_Init(USART1, &USART_InitStructure);// 第五步:开启中断并且初始化 NVIC,使能USART1接收中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);// 第六步:使能串口USART_Cmd(USART1, ENABLE);}// 第七步:编写中断处理函数:函数名格式为 USARTxIRQHandler(x 对应串口号)
// USART1中断服务程序
void USART1_IRQHandler(void)
{// 检查USART1是否接收到数据if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {// 读取接收到的字符char received_char = USART_ReceiveData(USART1);// 存储接收到的字符串的缓冲区static char buffer[10];static uint8_t index = 0;if (received_char != '\n' && received_char != '\r')//接收数据的结束标志{// 将字符存储到缓冲区buffer[index] = received_char;index++;} else{//将字符串末尾设置为'\0'buffer[index] = '\0';index = 0; //置0,为下一次存储做准备// 检查接收到的字符串是否为"open"if (strcmp(buffer, "open") == 0) {// 点亮LED灯GPIO_ResetBits(GPIOF, GPIO_Pin_9| GPIO_Pin_10); }}}
}//对printf()函数进行重定向
int fputc(int ch, FILE *f)
{USART_SendData(USART1,ch); //通过串口发送数据,每次发送一个字符while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕return ch;
}
注意:因为我们在程序上面设置了必须输入回车或者换行,串口才认可接收到的数据,所以必须在发送数据后再发送一个回车符, 这里 XCOM 提供的发送方法是通过勾选发送新行实现,如图,只要勾选了这个选项,每次发送数据后,XCOM 都会自动多发一个回车(0X0D+0X0A)。设置好了发送新行,我们再在发送区输入发送的命令:open,然后单击发送,这样灯便会点亮!

main.c代码:
#include <stdio.h>
#include "myusart.h"
#include "myled.h"int main(void)
{LED_Init();USART1_Config();while (1){// 主循环中无需处理接收数据,接收数据在中断中处理}
}

七、主从机通信的两种方式




至此,我们的本次的学习就结束了。通过以上几个实验,相信对串口通信有了深入的理解,这一节我们就讲解到这里,希望能对大家的开发有帮助。 如有兴趣,感谢点赞、关注、收藏,若有不正地方,还请各位大佬多多指教!
相关文章:
嵌入式开发十八:USART串口通信实验
上一节我们学习了串口通信的基本理论,串口通信是学习单片机的一个重要的一步,非常重要,这一节我们通过实验来学习串口通信的使用,以及串口的接收中断的使用。 一、发送单个字节uint8_t数据或者字符型数据 实现的功能:…...
redis复习
redis知识点 redis持久化redis 订阅发布模式redis主从复制哨兵模式redis雪崩,穿透缓存击穿(请求太多,缓存过期)缓存雪崩 redis持久化 redis是内存数据库,持久化有两种方式,一种是RDB(redis dat…...
SUSE linux的快照和恢复
snapper用于创建和管理文件系统快照,并在需要时实现回滚,它还可以用于创建用户数据的磁盘备份。snapper使用btrfs文件系统或者精简配置的被格式化成XFS或EXT4的LVM卷。snapper可以通过命令行或YaST来进行管理。 btrfs是一种copy-on-write文件系统&#x…...
【Qt快速入门(六)】- QLineEdit按钮的使用
目录 Qt快速入门(六)- QLineEdit按钮的使用QLineEdit按钮的使用QLineEdit的基本用法1. 创建和设置文本2. 获取输入文本3. 清空输入文本 文本处理1. 选择文本2. 设置光标位置3. 撤销和重做 输入验证1. 输入掩码2. 校验器3. 输入限制 样式设置1. 设置字体和…...
常用损失函数详解:广泛使用的优化约束方法
各类常用损失函数详解:广泛使用的优化约束方法 今天介绍下损失函数,先介绍下我常用的方法SmoothedL1,它是一个平滑的L1 penalty函数,用于处理约束violation。 标准的L1 penalty函数定义为: L 1 ( x ) { 0 , if x ≤ 0 x , if x > 0 …...
鸿蒙开发组件:【创建DataAbility】
创建DataAbility 实现DataAbility中Insert、Query、Update、Delete接口的业务内容。保证能够满足数据库存储业务的基本需求。BatchInsert与ExecuteBatch接口已经在系统中实现遍历逻辑,依赖Insert、Query、Update、Delete接口逻辑,来实现数据的批量处理。…...
配电室数据中心巡检3d可视化搭建的详细步骤
要搭建配电室巡检的3D可视化系统,可以按照以下步骤进行: 收集配电室数据: 首先,需要收集配电室的相关数据,包括配电室的布局、设备信息、传感器数据等。可以通过实地调查、测量和设备手册等方式获取数据。 创建3D模型…...
TIME_WAIT的危害
前言 该文章主要讨论下TIME_WAIT的存在意义和潜在危害,以及解决措施。 具体内容 首先看一下下面这幅图 这幅图来自《TCP IP详解卷1:协议 原书第2版中文》TCP状态变迁图。 TIME_WAIT存在意义 可靠的终止TCP连接。 保证让迟来的TCP报文有足够的时间被…...
搜维尔科技邀您共赴2024第四届轨道车辆工业设计国际研讨会
会议内容 聚焦“创新、设计、突破”,围绕“面向生命健康、可持续发展的轨道交通系统” 为主题,从数字化、智能化、人性化、绿色发展等方面,探索轨道交通行业的设计新趋势及发展新机遇。 举办时间 2024年7月10日-12日 举办地点 星光岛-青岛融…...
智能中人类造成的风险、机器造成的风险、环境造成的风险
在使用智能技术时,可能会面临各种类型的风险。以下是一些可能的风险情况: 1、人类造成的风险 错误判断和决策:人类在使用智能系统时可能会因为各种原因做出错误的判断和决策,导致不良后果。人为错误:技术操作人员、维护…...
MYSQL基础查询
示例:user_profile iddevice_idgenderageuniversityprovince12138male21北京大学Beijing23214male复旦大学Shanghai36543female20北京大学Beijing42315female23浙江大学Zhejiang55432male25山东大学Shandong 查询所有列 select * from user_profile;查询…...
【Golang】Go 中的生产者-消费者模式
Go 中的生产者-消费者模式 来源:https://medium.com/@mm.nikfarjam/the-producer-consumer-pattern-in-go-cf97299a0320 文章目录 Go 中的生产者-消费者模式介绍关键组件在 Go 中的实现结论Go 中的生产者-消费者模式 介绍 生产者-消费者模式是处理大数据的最常见设计模式之一…...
【通过新能源汽车的智慧数字底盘技术看计算机的相关技术堆栈?以后是软硬结合的全能程序员的天下,取代全栈(前后端都会的全栈程序员)】
汽车的“智慧数字底盘”是一个综合性的技术平台,旨在提升车辆的性能、安全性和驾驶体验。它集成了多种先进的技术和系统,是全能程序员的必杀技! 1. 传感器技术 a. 激光雷达(LiDAR) 用于生成高分辨率的3D地图&#…...
Python网络爬虫4-实战爬取pdf
1.需求背景 爬取松产品中心网站下的家电说明书。这里以冰箱为例:松下电器-冰箱网址 网站分析: 第一步: 点击一个具体的冰箱型号,点击了解更多,会打开此型号电器的详情页面。 第二步:在新打开的详情页面中…...
超神级!Markdown最详细教程,程序员的福音
超神级!Markdown最详细教程,程序员的福音Markdown最详细教程,关于Markdown的语法和使用就先讲到这里,如果喜欢,请关注“IT技术馆”。馆长会更新最实用的技术!https://mp.weixin.qq.com/s/fNzhLFyYRd3skG-…...
Android OTA 升级基础知识详解+源码分析
前言: 本文仅仅对OTA升级的几种方式的概念和运用进行总结,仅在使用层面对其解释。需要更详细的内容我推荐大神做的全网最详细的讲解: https://blog.csdn.net/guyongqiangx/article/details/129019303?spm1001.2014.3001.5502 三种升级方式…...
【吊打面试官系列-Mysql面试题】SQL 语言包括哪几部分?每部分都有哪些操作关键字?
大家好,我是锋哥。今天分享关于 【SQL 语言包括哪几部分?每部分都有哪些操作关键字?】面试题,希望对大家有帮助; SQL 语言包括哪几部分?每部分都有哪些操作关键字? SQL 语言包括数据定义(DDL)、…...
Redis的缓存击穿与解决
缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的Key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。 Redis实战篇 | Kyles Blog (cyborg2077.github.io) 目录 解决方案 互斥锁 实现 逻辑过期 实现 解决方案…...
网络层 IP协议【计算机网络】【协议格式 || 分片 || 网段划分 || 子网掩码】
博客主页:花果山~程序猿-CSDN博客 文章分栏:Linux_花果山~程序猿的博客-CSDN博客 关注我一起学习,一起进步,一起探索编程的无限可能吧!让我们一起努力,一起成长! 目录 一,前提 二&…...
Python学习笔记14:进阶篇(三)。类的终结篇,类的导入和模块的导入。
前言 这篇文章属于类知识的最后一篇,带一点点其他知识,学习内容来自于Python crash course。 关注我私信发送Python crash course,分享一份中文版PDF。 类的导入 在学习的时候,包括之前,我都是在一个文件中把所有代…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
