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

news/2024/4/29 15:59:00

电路连接:        

        连接显示屏模块,显示屏的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.mrgr.cn/p/74312410

相关文章

使用DBever连接人大金仓数据库

下载安装DBever首先需要下载并安装DBever,可以在DBever官网上下载最新版的安装程序,根据提示进行安装即可。 打开人大金仓数据库服务 在连接人大金仓数据库之前,需要确保人大金仓数据库服务已经启动。可以在服务列表中找到人大金仓数据库服务,并启动它。使用DBever连接人大…

【数据库】postgresql截取最后一个字符之前的所有字符,如V1.0.0.20230731110947中取V1.0.0

在PostgreSQL中,我们可以使用position函数和split_part函数来截取最后一个.之前的所有字符。这两个函数都非常有用,尤其是在处理文本数据时。 position函数 position函数用于查找一个字符串中某个子串的位置。它的语法如下: POSITION(substring IN string)其中,substring是…

DER编码

一、任务详情参考附件中图书p120 中7.1的实验指导,完成DER编码 Name实例中,countryName改为“CN”,organization Name-"你的学号" commoaName="你的姓名拼音" 用echo -n -e "编码" > 你的学号.der中,用OpenSSL asn1parse 分析编码的正确性…

揭秘’在家答答题,无需经验、无论男女、单号轻松日产200+的一个玩法

项目简介 公众号&#xff1a;老A程序站 这个项目是人人可参与的&#xff0c;无需支付任何费用&#xff0c;只需投入时间即可。每天的任务主要是回答问题。 项目 地 址 &#xff1a; laoa1.cn/1457.html 如果遇到不会的问题&#xff0c;可以直接使用百度进行搜索。我们通过…

YOLOF:单层特征检测也可以比FPN更出色 | CVPR 2021

论文通过分析发现FPN的成功在于divide-and-conquer策略解决了目标检测的优化问题,借此研究设计了仅用单层特征预测的高效检测网络YOLOF。YOLOF在结构上没有很多花哨的结构,却在准确率、推理速度和收敛速度上都有不错的提升,相对于眼花缭乱的FPN魔改结构,十分值得学习 来源:…

原型链-(前端面试 2024 版)

来讲一讲原型链 原型链只存在于函数之中 四个规则 1、引用类型&#xff0c;都具有对象特性&#xff0c;即可自由扩展属性。 2、引用类型&#xff0c;都有一个隐式原型 __proto__ 属性&#xff0c;属性值是一个普通的对象。 3、引用类型&#xff0c;隐式原型 __proto__ 的属…

RSTP环路避免实验(华为)

思科设备参考&#xff1a;RSTP环路避免实验&#xff08;思科&#xff09; 一&#xff0c;技术简介 RSTP (Rapid Spanning Tree Protocol) 是从STP发展而来 • RSTP标准版本为IEEE802.1w • RSTP具备STP的所有功能&#xff0c;可以兼容STP运行 • RSTP和STP有所不同 减少了…

TitanIDE与传统 IDE 比较

与传统IDE的比较 TitanIDE 和传统 IDE 属于不同时代的产物&#xff0c;在手工作坊时代&#xff0c;一切都是那么的自然&#xff0c;开发者习惯 Windows 或 MacOS 原生 IDE。不过&#xff0c;随着时代的变迁&#xff0c;软件行业已经步入云原生时代&#xff0c;TitanIDE 是顺应…

使用MergeKit创建自己的专家混合模型:将多个模型组合成单个MoE

由于Mixtral的发布,专家混合(MoE)架构在最近几个月变得流行起来。虽然Mixtral和其他MoE架构是从头开始预训练的,但最近出现了另一种创建MoE的方法:Arcee的MergeKit库可以通过集成几个预训练模型来创建moe。这些人通常被称为frankenMoEs或MoErges,以区别于预先训练的MoEs。 …

Redis命令-List命令

4.6 Redis命令-List命令 Redis中的List类型与Java中的LinkedList类似&#xff0c;可以看做是一个双向链表结构。既可以支持正向检索和也可以支持反向检索。 特征也与LinkedList类似&#xff1a; 有序元素可以重复插入和删除快查询速度一般 常用来存储一个有序数据&#xff…

前端学习-UI框架学习-Bootstrap5-011-徽章(Badges)

菜鸟教程链接 <template><div class="container mt-3"><h3>徽章<span class="badge bg-success">new</span></h3><h3>药丸形状徽章<span class="badge bg-danger rounded-pill">new</span&g…

Acwing 1491. 圆桌座位

https://www.acwing.com/problem/content/1493/输入样例1: 4 1 1 2 输出样例1: 2 输入样例2: 10 5 1 2 3 4 5 6 7 8 9 10 输出样例2: 112512#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> PII; const LL MAXN=1e…

多源统一视频融合可视指挥调度平台VMS/smarteye系统概述

系统功能 1. 集成了视频监控典型的常用功能&#xff0c;包括录像&#xff08;本地录像、云端录像&#xff08;录像计划、下载计划-无线导出&#xff09;、远程检索回放&#xff09;、实时预览&#xff08;PTZ云台操控、轮播、多屏操控等&#xff09;、地图-轨迹回放、语音对讲…

网络:udptcp套接字

目录 协议 网络传输基本流程 网络编程套接字 udp套接字编程 udp相关代码实现 sock函数 bind函数 recvfrom函数 sendto函数 udp执行指令代码 popen函数 udp多线程版收发消息 tcp套接字编程 tcp套接字代码 listen函数 accept函数 read/write函数 connect函数 recv/…

3.29

这几天GGrun兽性大发,D真和int_R都受到了非人的礼遇(指猜拳决斗) GGrun:来!进行决斗!(指猜拳) (猜拳输了) GGrun:hinhin,baby~~ man~~(搓手,跃跃欲试) (但GGrun的主要攻击对象是CTH)二调下来了,被文化课薄纱了 但GGrun这个出生数学考了149,在这里强烈谴责GGru…

verilog语言学习-HDLBits

基本逻辑: and  a&b  与  同为1时输出1or   a|b  或  有一个为1输出1xor   a^b  异或  ab不同时输出1nand   !(a&b)  与非  not and a与b再取反nor   ~(a|b)  或非  a或b再取反xnor  a~^b  同或  ab相同时输出1anotb  a&(…

fastadmin学习04-一键crud

FastAdmin 默认内置一个 test 表&#xff0c;可根据表字段名、字段类型和字段注释通过一键 CRUD 自动生成。 create table fa_test (id int unsigned auto_increment comment ID primary key,user_id int(10) default 0 null…

Web Components使用(一)

在使用Web Components之前&#xff0c;我们先看看上一篇文章Web Components简介&#xff0c;其中提到了相关的接口、属性和方法。 正是这些接口、属性和方法才实现了Web Components的主要技术&#xff1a;Custom elements&#xff08;自定义元素&#xff09;、Shadow DOM&#…

百度智能云千帆,产业创新新引擎

本文整理自 3 月 21 日百度副总裁谢广军的主题演讲《百度智能云千帆&#xff0c;产业创新新引擎》。 各位领导、来宾、媒体朋友们&#xff0c;大家上午好。很高兴今天在石景山首钢园&#xff0c;和大家一起沟通和探讨大模型的发展趋势&#xff0c;以及百度最近一段时间的思考和…

2.7、创建列表(List)

概述 列表是一种复杂的容器&#xff0c;当列表项达到一定数量&#xff0c;内容超过屏幕大小时&#xff0c;可以自动提供滚动功能。它适合用于呈现同类数据类型或数据类型集&#xff0c;例如图片和文本。在列表中显示数据集合是许多应用程序中的常见要求&#xff08;如通讯录、…