嵌入式开发十八: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。 类的导入 在学习的时候,包括之前,我都是在一个文件中把所有代…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...

免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...

基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...