STM32使用USART发送数据包指令点亮板载LED灯

电路连接:        

        连接显示屏模块,显示屏的SCL在B10,SDA在B11。

程序目的:

        发送@LED_ON指令打开板载LED灯,发送@LED_OFF关闭板载LED灯,与上一个博客不同,这个实际上是实现串口收发文本数据包。

开始编程:

Serial.c

初始化GPIO与中断

  • 初始化A9引脚,设置为复用推挽输出,也就是让内部硬件控制引脚
  • 初始化A10引脚,设置为浮空输入或上拉输入,这里使用上拉输入,具有较好的抗干扰能力
  • 不使用硬件流控制,也就是不使用RTS,CTS等
  • 串口模式为TX|RX(Transform)|(Receive)表示发送和接收
  • 无校验位,可选择奇校验,偶校验等
  • 1位停止位,可选择0.5 1 1.5 2这几个
  • 8字长,不需要校验选8位,需要选9位
  • 开启RXNE(RX No Empty)到NVIC的输出,也就是开启中断
  • 配置中断
void Serial_Init() {RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//开启时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启时钟GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//浮空输入或者上拉输入,使用上拉输入抗干扰能力更强GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 9600;//波特率USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制(不使用,CTS,CTS&RTS)USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//串口模式 可以使用(或)|符号实现Tx和Rx同时设置USART_InitStructure.USART_Parity = USART_Parity_No;//校验位,无需校验USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位,选择1位USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长USART_Init(USART1, &USART_InitStructure);//串口接收部分可以采用查询或者中断的方式,如果采用中断就需要在这里配置NVIC//开启中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启RXNE到NVIC的输出NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStructure);USART_Cmd(USART1, ENABLE);//开启USART
}

中断函数:

状态机如图:

        这里的中断函数与HEX数据包不同,当收到@字符时转为第一个状态,接收数据,由于这个数据不是固定包长,那么收到\r就进入状态2,再收到\n表示接收完成,进入状态0。这里如果包尾不是两个字符的话,只需要设置两个状态即可。

        由于是字符串,因此在状态2转移到状态0时,需要加上字符串的自带的'\0',这样才能定义字符串跟接受到的字符串比较。

        还需要建立两个全局变量,char Serial_RxPacket[100];uint8_t Serial_RxFlag;一个是存放接受的数据,一个是存放接收数据标志位。

        在中断函数中,定义两个静态变量,类似全局变量,函数进入只会初始化一次0,函数退出仍然有效,与全局函数不同,静态变量只能在本函数中使用,这两个静态变量:static uint8_t RxState = 0;static uint8_t pRxPacket = 0;一个用于定位状态,一个用于定位接收到的数据。

中断函数代码:

char Serial_RxPacket[100];uint8_t Serial_RxFlag;
void USART1_IRQHandler() {static uint8_t RxState = 0;//类似全局变量,函数进入只会初始化一次0,函数退出仍然有效,与全局函数不同,静态变量只能在本函数中使用static uint8_t pRxPacket = 0;if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET) {//如果读取DR就自动清除标志位,如果没有就需要手动清除uint8_t RxData = USART_ReceiveData(USART1);if(RxState == 0){//若在这里将RxState置为1,那么下面就会立马执行,因此要加上else,也可用switch case语句if(RxData == '@') {RxState = 1;pRxPacket = 0;}}else if(RxState == 1) {if(RxData == '\r'){RxState = 2;}else {Serial_RxPacket[pRxPacket] = RxData;pRxPacket ++;}}else if(RxState ==  2){if(RxData == '\n') {RxState = 0;Serial_RxFlag = 1;Serial_RxPacket[pRxPacket] = '\0';//不加不能使用OLED_ShowString}}USART_ClearITPendingBit(USART1, USART_IT_RXNE);}
}

Serial.c整体代码

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>
char Serial_RxPacket[100];uint8_t Serial_RxFlag;void Serial_Init() {RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//开启时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启时钟GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//浮空输入或者上拉输入,使用上拉输入抗干扰能力更强GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 9600;//波特率USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制(不使用,CTS,CTS&RTS)USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//串口模式 可以使用(或)|符号实现Tx和Rx同时设置USART_InitStructure.USART_Parity = USART_Parity_No;//校验位,无需校验USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位,选择1位USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长USART_Init(USART1, &USART_InitStructure);//串口接收部分可以采用查询或者中断的方式,如果采用中断就需要在这里配置NVIC//开启中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启RXNE到NVIC的输出NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStructure);USART_Cmd(USART1, ENABLE);//开启USART
}
void Serial_SendByte(uint8_t Byte) {USART_SendData(USART1, Byte);//发送数据while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET) {//等待发送寄存器空,//TXE就是发送寄存器空的标志位,不需要手动清零,下一次发送数据时候会自动清零}
}
void Serial_SendArray(uint8_t *Array, uint16_t Length){uint16_t i;for(int i = 0; i < Length; i++) {Serial_SendByte(Array[i]);}}
void Serial_SendString(char *Str) {//字符串自带结束标志位uint8_t i;for(int i = 0; Str[i] != '\0'; i++) {Serial_SendByte(Str[i]);}}
uint32_t Serial_Pow(uint32_t X, uint32_t y) {uint32_t Result = 1;while(y--) {Result *= X;}return Result;
}
void Serial_SendNumber(uint32_t Number, uint8_t Length) {uint8_t i;for(int i = 0; i < Length; i++){Serial_SendByte((Number / Serial_Pow(10, Length - i - 1)) % 10 + '0');}}
int fputc(int ch, FILE* f){Serial_SendByte(ch);//重定向到串口,使得Printf打印到串口return ch;}
//使用sprintf让其他的串口也能使用,sprintf可以把格式化字符输出到一个字符串里
void Serial_Printf(char* format,...){//三个点用来接收后面可变参数列表char String[100];va_list arg;va_start(arg, format);//从format位置开始接收参数表,放在arg里面vsprintf(String, format, arg);va_end(arg);Serial_SendString(String);
}
uint8_t Serial_GetRxFlag() {if(Serial_RxFlag == 1){Serial_RxFlag = 0;return 1;}return 0;
}
void Serial_SendPacket(){}
void USART1_IRQHandler() {static uint8_t RxState = 0;//类似全局变量,函数进入只会初始化一次0,函数退出仍然有效,与全局函数不同,静态变量只能在本函数中使用static uint8_t pRxPacket = 0;if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET) {//如果读取DR就自动清除标志位,如果没有就需要手动清除uint8_t RxData = USART_ReceiveData(USART1);if(RxState == 0){//若在这里将RxState置为1,那么下面就会立马执行,因此要加上else,也可用switch case语句if(RxData == '@') {RxState = 1;pRxPacket = 0;}}else if(RxState == 1) {if(RxData == '\r'){RxState = 2;}else {Serial_RxPacket[pRxPacket] = RxData;pRxPacket ++;}}else if(RxState ==  2){if(RxData == '\n') {RxState = 0;Serial_RxFlag = 1;Serial_RxPacket[pRxPacket] = '\0';//不加不能使用OLED_ShowString}}USART_ClearITPendingBit(USART1, USART_IT_RXNE);}
}

Serial.h

源代码:

#ifndef __SERIAL_H
#define __SERIAL_H
#include <stdio.h>
extern char Serial_RxPacket[];void Serial_Init();
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char* format,...);
uint8_t Serial_GetRxFlag();#endif

GpioControl.c:

        编写GPIO控制函数,封装GPIO引脚的初始化和控制功能。

#include "stm32f10x.h"                  // Device header
void GpioInit(GPIO_TypeDef *GPIOx, uint16_t Pin, GPIOMode_TypeDef GpioMode){uint32_t RCC_APB2Periph_GPIOx;if(GPIOx == GPIOA) {RCC_APB2Periph_GPIOx = RCC_APB2Periph_GPIOA;}else if(GPIOx == GPIOB) {RCC_APB2Periph_GPIOx = RCC_APB2Periph_GPIOB;}else if(GPIOx == GPIOC) {RCC_APB2Periph_GPIOx = RCC_APB2Periph_GPIOC;}RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOx, ENABLE);//ctrl + Alt + 空格:可以出现代码提示GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GpioMode;//推挽输出GPIO_InitStructure.GPIO_Pin = Pin;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOx, &GPIO_InitStructure);GPIO_ResetBits(GPIOx, Pin);
}
void GpioTurn(GPIO_TypeDef *GPIOx, uint16_t GPIO_PIN) {//反转当前引脚状态if(GPIO_ReadOutputDataBit(GPIOx,GPIO_PIN) == 0){GPIO_SetBits(GPIOx,GPIO_PIN);}else{GPIO_ResetBits(GPIOx, GPIO_PIN);}
}
void GpioControl(GPIO_TypeDef *GPIOx, uint16_t GPIO_PIN, uint8_t sign) {//控制引脚if(sign == ENABLE){GPIO_SetBits(GPIOx, GPIO_PIN);}if(sign == DISABLE){GPIO_ResetBits(GPIOx, GPIO_PIN);}
}

GpioControl.h:

#ifndef __GPIOCONTROL_H
#define __GPIOCONTROL_Hvoid GpioInit(GPIO_TypeDef *GPIOx, uint16_t Pin, GPIOMode_TypeDef GpioMode);
void GpioTurn(GPIO_TypeDef *GPIOx, uint16_t GPIO_PIN);
void GpioControl(GPIO_TypeDef *GPIOx, uint16_t GPIO_PIN, uint8_t sign);#endif 

main.c

        在main函数中,主要逻辑就是判断标志位来得到是否有数据接收,若有则跟指令进行对比,如果是打开灯指令,那么就置C13引脚为低电平并发送LED_ON_OK指令,点亮LED灯。若是关灯指令,那么就置引脚为高电平,关闭LED灯并发送LED_OFF_OK,若都不是,那么就输出ERROR_CMD指令表示指令错误。

主要代码如下:

#include "stm32f10x.h"                  // Device header
#include "DELAY.h"
#include "OLED.h"
#include "Serial.h"
#include "GpioControl.h"
#include <string.h>
uint8_t RxData;
uint8_t KeyNum;int main() {GpioInit(GPIOC, GPIO_Pin_13, GPIO_Mode_Out_PP);GPIO_SetBits(GPIOC,GPIO_Pin_13);OLED_Init();Serial_Init();OLED_ShowString(1, 1, "TxData:");OLED_ShowString(3, 1, "RxData:");while(1){if(Serial_GetRxFlag() == 1) {OLED_ShowString(4,1, "                ");//清除第四行OLED_ShowString(4,1, Serial_RxPacket);if(strcmp(Serial_RxPacket,  "LED_ON") == 0) {GpioControl(GPIOC, GPIO_Pin_13, DISABLE);Serial_SendString("LED_ON_OK\r\n");OLED_ShowString(2,1,"                ");OLED_ShowString(2,1,"LED_ON_OK");}else if(strcmp(Serial_RxPacket,  "LED_OFF") == 0) {GpioControl(GPIOC, GPIO_Pin_13, ENABLE);Serial_SendString("LED_OFF_OK\r\n");OLED_ShowString(2,1,"LED_OFF_OK");}else {Serial_SendString("ERROR_CMD\r\n");OLED_ShowString(2,1,"                ");OLED_ShowString(2,1,"ERROR_CMD");}}}
}

程序现象:

 

程序及软件下载:

程序打包代码:程序包下载

串口助手下载:串口助手下载

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/293446.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端学习<二>CSS基础——14-CSS3属性详解:Web字体

前言 开发人员可以为自已的网页指定特殊的字体&#xff08;将指定字体提前下载到站点中&#xff09;&#xff0c;无需考虑用户电脑上是否安装了此特殊字体。从此&#xff0c;把特殊字体处理成图片的方式便成为了过去。 支持程度比较好&#xff0c;甚至 IE 低版本的浏览器也能…

【手册】——mq延迟队列

目录 一、背景介绍二、思路&方案三、过程1.项目为啥用延迟队列&#xff1f;2.项目为啥用三方延迟队列&#xff1f;3.项目中为啥用rabbitmq延迟队列&#xff1f;4.rabbitmq延迟队列的安装5.rabbitmq的延迟队列配置方式5.1.exchange配置5.2.queues配置5.3.exchange和queues的…

企业招聘,应用MBTI来做人才测评招聘测评

每年的校招季都是企业争抢优秀应届毕业生人才的忙碌季。只有精准识人用人&#xff0c;才能不断为企业注入新鲜活力和青春智慧。但是随着毕业生数量越来越多&#xff0c;企业如何在招聘中精准发现自己最需要的人才&#xff0c;成为摆在人力资源部门的大难题。人才测评是各企业都…

【蓝桥杯】矩阵快速幂

一.快速幂概述 1.引例 1&#xff09;题目描述&#xff1a; 求A^B的最后三位数表示的整数&#xff0c;A^B表示&#xff1a;A的B次方。 2&#xff09;思路&#xff1a; 一般的思路是&#xff1a;求出A的B次幂&#xff0c;再取结果的最后三位数。但是由于计算机能够表示的数字…

机器学习周报第35期

目录 一、文献阅读&#xff1a;You Only Look Once: Unified, Real-Time Object Detection1.1 摘要1.2 背景1.3 论文模型1.4 网络设计1.5 YOLO的局限性1.6 实现代码 target 7*7*30 值域为0-1 一、文献阅读&#xff1a;You Only Look Once: Unified, Real-Time Object Detection…

Redis入门到实战-第二十二弹

Redis入门到实战 Redis高可用Sentinel官网地址Redis概述虚拟机配置在主从复制环境的基础上添加Sentinel更新计划 Redis高可用Sentinel 官网地址 声明: 由于操作系统, 版本更新等原因, 文章所列内容不一定100%复现, 还要以官方信息为准 https://redis.io/Redis概述 Redis是一…

效率工具RunFlow完全手册之基础篇

RunFlow是我们开发的一款全新的效率工具&#xff0c;本文作为RunFlow操作手册和功能演示的基础篇&#xff0c;想了解我们有哪些新特性可以阅读我们的这篇文章&#xff0c;这里就不过多赘述了&#xff0c;我们直接开始。 关键字 关键字是我们的一个核心概念&#xff0c;一个功…

【WEEK5】 【DAY4】数据库操作【中文版】

2024.3.28 Thursday 目录 2.数据库操作2.1.数据库2.1.1.新建数据库&#xff08;右键的方法&#xff09;2.1.2.查询&#xff1a;点击“查询”->“新建查询表”即可输入所需要的语句&#xff0c;点击“运行”&#xff0c;如&#xff1a; 2.2.结构化查询语句分类2.3.数据库操作…

Mac 版 IDEA 中配置 GitLab

一、安装Git 在mac终端输入Git检测指令&#xff0c;可以通过git命令查看Git是否安装过&#xff0c;如果没有则会弹出安装按钮&#xff0c;如果安装过则会输出如下信息。 WMBdeMacBook-Pro:~ WENBO$ git usage: git [--version] [--help] [-C <path>] [-c namevalue][--…

PostgreSQL到Doris的迁移技巧:实时数据同步新选择!

PostgreSQL可以说是目前比较抢手的关系型数据库了&#xff0c;除了兼具多样功能和强大性能之外&#xff0c;还具备非常优秀的可扩展性&#xff0c;更重要的是它还开源&#xff0c;能火不是没有理由的。 虽然PostgreSQL很强大&#xff0c;但是它也有短板&#xff0c;相对于专业…

Netty实现文件服务器

1.文件上传下载的常用方法 文件上传下载是一种非常常见的功能&#xff0c;特别是在web服务网站。 常用的文件上传下载协议有以下几种&#xff1a; FTP&#xff08;File Transfer Protocol&#xff09;:是一种用于在计算机间传输文件的标准网络协议。它使用客户端-服务器架构…

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记12:DAC数模转换

系列文章目录 嵌入式|蓝桥杯STM32G431&#xff08;HAL库开发&#xff09;——CT117E学习笔记01&#xff1a;赛事介绍与硬件平台 嵌入式|蓝桥杯STM32G431&#xff08;HAL库开发&#xff09;——CT117E学习笔记02&#xff1a;开发环境安装 嵌入式|蓝桥杯STM32G431&#xff08;…

2024年天津体育学院退役大学生士兵专升本专业考试报名安排

天津体育学院2024年退役大学生士兵免试专升本招生专业考试报名安排 一、报名安排 1.报名对象&#xff1a;免于参加天津市文化考试的退役大学生士兵&#xff08;已参加天津市统一报名且资格审核通过&#xff09; 2.报名时间&#xff1a;2024年4月4日9&#xff1a;00-4月5日17…

Stream流 --java学习笔记

什么是Stream? 也叫Stream流&#xff0c;是|dk8开始新增的一套APl(java.util.stream.*)&#xff0c;可以用于操作集合或者数组的数据。优势:Stream流大量的结合了Lambda的语法风格来编程&#xff0c;提供了一种更加强大&#xff0c;更加简单的方式操作集合或者数组中的数据&a…

1.10 类、方法、封装、继承、多态、装饰器

一、介绍类 类(class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例 实例化&#xff1a;创建一个类的实例&#xff0c;类的具体对象。 对象&#xff1a;通过类定义的数据结构实例。对象包括两个数据成员&#x…

windows安全中心设置@WindowsDefender@windows安全中心常用开关

文章目录 abstractwindows defender相关服务&#x1f47a; 停用windows Defender临时关闭实时防护使用软件工具关闭defender control(慎用)dismdControl 其他方法使其他杀毒软件注册表修改 保护历史恢复被认为是有病毒的文件添加信任目录,文件,文件类型或进程 abstract window…

算法学习——LeetCode力扣动态规划篇4(377. 组合总和 Ⅳ、322. 零钱兑换、279. 完全平方数、139. 单词拆分)

算法学习——LeetCode力扣动态规划篇4 377. 组合总和 Ⅳ 377. 组合总和 Ⅳ - 力扣&#xff08;LeetCode&#xff09; 描述 给你一个由 不同 整数组成的数组 nums &#xff0c;和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。 题目数据保…

2、Cocos Creator 下载安装

Cocos Creator 从 v2.3.2 开始接入了全新的 Dashboard 系统&#xff0c;能够同时对多版本引擎和项目进行统一升级和管理&#xff01;Cocos Dashboard 将做为 Creator 各引擎统一的下载器和启动入口&#xff0c;方便升级和管理多个版本的 Creator。还集成了统一的项目管理及创建…

pytorch反向传播算法

目录 1. 链式法则复习2. 多输出感知机3. 多层感知机4. 多层感知机梯度推导5. 反向传播的总结 1. 链式法则复习 2. 多输出感知机 3. 多层感知机 如图&#xff1a; 4. 多层感知机梯度推导 简化式子把( O k O_k Ok​ - t k t_k tk​) O k O_k Ok​(1 - O k O_k Ok​)起个别名…

Python(django)之单一接口展示功能前端开发

1、代码 建立apis_manage.html 代码如下&#xff1a; <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><title>测试平台</title> </head> <body role"document"> <nav c…