[标准库]STM32F103R8T6 串口的收发

news2024/10/5 21:24:42

前言

这篇记录一下怎么调用标准库的函数来初始化一个串口,并调库实现发数据和收数据,以及串口收中断的使用。

越往深处学习越感觉其实32就是一个功能更加齐全和强大的MCU,其实跟51没有什么本质上的区别。很多设置的地方都是同质化的。比如需要用到某个中断,无非就是初始化一个结构体变量,然后给这个变量的成员配好初值再调用NVIC_Init这个初始化函数新建一个中断。

比如这里我需要用到一个串口接收中断,

static void USART1_NVIC_Config(void)
{
    NVIC_InitTypeDef NVIC_InitStruct;

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVIC_InitStruct);
}

新建一个NVIC_InitTypeDef的结构体变量,然后给这个变量的成员都配置好相应的值,我这里设置串口接收中断的中断优先级为分组2 主优先级为1 子优先级为1,然后写好当前要配置的是哪一个中断,当前配置的中断为USART1_IRQn,并且配置的这个中断是使能状态,最后调用NVIC(嵌套中断控制器)初始化函数NVIC_Init新建一个中断,并把刚刚配置的结构体的地址作为形参传入进去,库函数就会帮我们新建一个串口中断。

因为我是需要一个串口接收中断。根据手册描述,只要接收寄存器收到数据,USART_SR这个串口的状态寄存器中的RXNE(读数据寄存器非空 (Read data register not empty)) 这一位就会自动置一,如果开启了串口接收中断的话,这时候就会进入相应串口的中断服务函数。

写好配置串口中断的函数之后,在下面的串口初始化函数中调用它:

void USART1_Config(void)
{
    //初始化GPIO
    GPIO_InitTypeDef  GPIO_InitStruct;
    USART_InitTypeDef USART_InitStruct;

    //打开USART1 RX和TX的时钟 103R8T6 是PA9-TXD  PA10-RXD
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    //打开串口外设的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);

    //将PA9 改为推挽复用
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStruct);

    //将PA10 改为浮空输入
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStruct);

    USART_InitStruct.USART_BaudRate = 115200;       //波特率115200
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;        //字长8bit
    USART_InitStruct.USART_StopBits = USART_StopBits_1;             //停止位 1bit
    USART_InitStruct.USART_Parity = USART_Parity_No;                //不校验
    USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;    //发送和接收都使能
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;    //不使用硬件流控制

    USART_Init(USART1,&USART_InitStruct);

    USART1_NVIC_Config();           //配置串口中断的 分组、优先级等

    USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);

    USART_Cmd(USART1,ENABLE);
}

类似新建串口中断的方法,初始化串口的方法也大同小异。首先先新建两个结构体变量,一个用来配置IO,另一个用来配置串口。至于为什么调用一个Init的库函数就能新建好一个串口,这其实不用纠结,库函数其实本质上是根据用户传入的参来判断需要给内部的哪些寄存器赋怎样的值,如果我们使用LL库开发,那么这部分就需要用户自己来写,目前使用标准库,这种工作就交给库来做。

有个日后需要注意的地方,103R8T6的USART1是挂载在APB2时钟线上的,所以在配置USART1这个串口的时候需要开APB2这条时钟线上的USART1外设的时钟;USART2-5都是挂载在APB1这条时钟线上的,所以在使用串口2-5的时候要注意不要开错了时钟。

配置Pin脚就是调用GPIO_Init,配置串口就是调用USART_Init,不过USART_Init这个函数的第一个形参,库已经帮我们新建好了,直接填需要用哪一个USART就行。

USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); 这一句就是配置串口中断的,因为能触发串口中断有很多种方法,比如发送寄存器空 中断、发送完成 中断、接收寄存器非空 中断 等等…这里就是把串口中断配置成 只有接收寄存器非空 产生一次中断 ,推测如果再同时开启一个 发送完成 中断,当两个条件任意成立一个,就会进入串口1中断服务函数里面,那么在服务函数里面就需要判断一下到底是哪个中断标志成立了产生的中断,再做相应的处理。这个功能就比51强大很多的,不同的组合可以玩出很多不同的效果。

这里还要另说一句,发送寄存器空了 并不代表数据就已经全部发送出去了,发送数据时,MCU会把数据先丢到发送寄存器去,然后发送寄存器再把数据丢到发送移位寄存器去,然后发送移位寄存器再把数据发送到编码模块发给上位机。框图如下
在这里插入图片描述
最后就是开启串口这个外设的库函数 USART_Cmd(USART1,ENABLE);
相应的关闭串口就是 USART_Cmd(USART1,DISABLE);

在主函数初始化的时候调用这个串口初始化函数USART1_Config,就可以把串口外设开起来了。

此时上位机发送一次数据,串口接收到数据之后就会产生一次中断,中断服务函数就可以写为:

void USART1_IRQHandler(void)
{
    uint8_t rece_data=0;
    if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == SET)
    {
        rece_data = USART_ReceiveData(USART1);
        USART_SendData(USART1,rece_data);
		while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET) {;}
    }
}

中断回调函数的名字 USART1_IRQHandler 这个是在启动文件里面规定好的,如果自己没有写,库会默认帮我们编译库里面弱定义好的回调函数,内容就是while(1) 即一直停留在回调函数里面。

这个回调函数的内容就是,判断一下接收寄存器非空 这一位是否被置1了(即接收到数据了),如果确定收到数据,那么就调用USART_ReceiveData函数把数据从 数据寄存器(USART_DR) 这里面取出来,当我们在读USART_DR这个寄存器的时候,RXNE这一位自动清零。
在这里插入图片描述
把数据读出来之后,调用串口发送函数USART_SendData,把刚刚接收到的数据通过串口1又发送出去,发送期间一直调用USART_GetFlagStatus 获取一下串口1的USART_SR的TC位(发送完成标志位)是否还是0,如果为0则是没法送完,就一直阻塞在这等串口发送完毕,当串口发送完毕之后,发送完成标志位被置一,跳出循环。同时我们就可以在上位机的接收窗口看到刚刚发送了什么数据过去给MCU。

到这一步串口的基础功能就实现完毕了,野火教程里面根据上位机发送的内容来点亮不同的灯,其实就是用switch(或说是if else)根据收到的数据判断一下然后再把相应的IO拉高或是拉低,没什么难度就不写了。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JVM知识点整理(整理中)

JVM知识点整理1、JVM与java体系结构1.1、java的体系结构1.2、JVM1.2.1、从跨平台的语言到跨语言的平台1.2.2、常用的JVM实现1.2.3、JVM的位置1.2.4、JDK、JER、JDK1.2.5、JVM的整体结构1.2.6、java代码的执行流程1.2.7、JVM的代码模型1.2.8、JVM的生命周期2、类加载子系统2.1、…

ARM NandFlash 介绍

一、NandFlash 的接口 1、Nand 的型号与命名 (1) Nand 的型号命名都有含义,就拿 K9F2G08 来示例分析一下:K9F 表示是三星公司的 NandFlash 系列。2G 表示 Nand 的大小是 2Gbit(256MB)。08 表示 Nand 是 8 位的( 8 位…

员工入职管理系统|员工管理系统|基于SpringBoot+Vue的企业新员工入职系统

作者主页:编程指南针 作者简介:Java领域优质创作者、CSDN博客专家 、掘金特邀作者、多年架构师设计经验、腾讯课堂常驻讲师 主要内容:Java项目、毕业设计、简历模板、学习资料、面试题库、技术互助 收藏点赞不迷路 关注作者有好处 文末获取源…

SICTF2023 WP

前言 新年前的最后一场比赛&#xff0c;感谢shenghuo2师傅提供的misc和密码的wp&#xff0c;把misc和密码ak了&#xff0c;太强了 web 兔年大吉 源码 <?php highlight_file(__FILE__); error_reporting(0);class Happy{private $cmd;private $content;public function _…

Registration Center

CAP●一致性(Consistency)&#xff1a;所有节点在同一时间具有相同的数据&#xff1b;●可用性(Availability) &#xff1a;保证每个请求不管成功或者失败都有响应&#xff1b;某个系统的某个节点挂了&#xff0c;但是并不影响系统的接受或者发出请求。●分隔容忍(Partition to…

python循环语句

Python循环语句 文章目录Python循环语句一、实验目的二、实验原理三、实验环境四、实验内容五、实验步骤1.While循环结构2.While无限循环3.For循环语法4.break语句和continue语句一、实验目的 掌握循环结构的语法 二、实验原理 Python中的循环语句有 for 和 while。 Python…

AcWing蓝桥杯AB组辅导课07、贪心

文章目录前言一、贪心模板题例题1&#xff1a;AcWing 104. 货仓选址&#xff08;贪心&#xff0c;简单&#xff0c;算法竞赛进阶指南&#xff09;分析题解&#xff1a;贪心思路例题例题1&#xff1a;AcWing 1055. 股票买卖 II&#xff08;贪心、状态机&#xff0c;简单&#xf…

[ESP][驱动]GT911 ESP系列驱动

GT911ForESP GT911在ESP系列上的驱动&#xff0c;基于IDF5.0&#xff0c;ESP32S3编写 本库使用面向对象思想编写&#xff0c;可创建多设备多实例 Github&#xff0c;Gitee同步更新&#xff0c;Gitee仅作为下载仓库&#xff0c;提交Issue和Pull request请到Github Github: h…

具体芯片的I2C_Adapter驱动分析

具体芯片的I2C_Adapter驱动分析 文章目录具体芯片的I2C_Adapter驱动分析参考资料&#xff1a;一、 I2C控制器内部结构1.1 通用的简化结构1.2 IMX6ULL的I2C控制器内部结构二、 I2C控制器操作方法三、 分析代码3.1 设备树3.2 驱动程序分析致谢参考资料&#xff1a; Linux内核真正…

03_筛选标记2.0版和3.0版FIND及ColorIndex

文章目录2.0版工作簿筛选标记筛选sheet标记取消筛选标记3.0版ColorIndex 下标代码特别鸣谢,大佬的分享FIND方法的使用2.0版 工作簿筛选标记 Option Explicit Sub 自动筛选()Dim Town As StringDim wsh As WorksheetCall 初始化 初始化表格状态Town InputBox("请输入街…

SLAM笔记——turtlebot传感器ekf实验实验

这里写目录标题实验内容实验准备msg数据类型给uwb和odom增加噪声robot_pose_ekf发布路径实验结果实验内容 本实验将在gazebo仿真环境中使用ekf进行传感器数据融合。本文使用turtlebot3进行实验&#xff0c;turtlebot本身会发布odom和imu。imu的误差可以在urdf文件中进行调整&a…

追梦之旅【数据结构篇】——对数据结构的认知 + 初识时间复杂度和空间复杂度~

详解C语言函数模块知识(下篇&#xff09;&#x1f60e;前言&#x1f64c;浅谈数据结构~&#x1f64c;1、什么是数据结构&#xff1f;(ˇˍˇ) 想&#xff5e;2、什么是算法&#xff1f;ˇˍˇ) 想&#xff5e;3、数据结构和算法的重要性&#x1f60a;4、如何才能学好数据结构呢…

初识 NodeJS(基于 Chrome V8 引擎的 JavaScript 运行时环境)

初识 NodeJS&#xff08;基于 Chrome V8 引擎的 JavaScript 运行时环境&#xff09;参考描述NodeJSNodeJS 可以做什么&#xff1f;特点用武之地获取检测运行JavaScript 运行时环境JavsScript 引擎浏览器中的 JavaScript 运行时环境Chrome 浏览器运行时环境NodeJS 中的 JavaScri…

【着色器实现海面效果_菲尼尔/Unlit/Transparent】

1.水体颜色 2.反射,水面波纹流动 3.折射、水底、水底透明度和折射 4.焦散,在水底接近岸边的水域 5.岸边泡沫,水花接近岸边的泡沫 6.水体运动,顶点动画 用灯光模式是Light Model :Unilt Render Type:Transparent 获取水面深度 利用这个节点,从深度图获取世界空间的位…

如何做流程图?这几个实用的制作流程图方法分享给你

说到流程图的制作&#xff0c;相信大家都并不陌生&#xff0c;在日常的工作和学习中&#xff0c;我们都会根据需求接触到各种各样的流程图&#xff0c;有时还要自己动手绘制流程图并使用&#xff0c;但你是否会因为不会绘制流程图而感到苦恼呢&#xff1f;没关系&#xff0c;今…

vue中利用ref实现更灵活的子向父传值

目录前言一&#xff0c;基础代码二&#xff0c;层次递进的讲解用法2.1 给子组件设置ref2.2 自定义事件2.3 给子组件设置一个自定义事件三&#xff0c;灵活性四&#xff0c;注意后记前言 目前我们熟知的子向父传值有两种方式&#xff1a; 一种是在父组件中定义函数&#xff0c;…

【AI】Windows配置GPU Cuda驱动和Pytorch框架

目录 1、Cuda驱动安装 1.1 查看显卡硬件 1.2 查看cuda版本 2、Annaconda python环境准备 2.1 创建pytorch_gpu 2.2 查看python版本 3、Pytorch和torchVsion软件安装 4、验证测试 在进行AI项目开发的时候&#xff0c;经常要在GPU环境中运行代码&#xff0c;对于没有配置…

动手深度学习-pytorch线性代数

标量简单操作长度向量简单操作长度其他操作矩阵简单操作乘法&#xff08;矩阵*向量&#xff09;乘法&#xff08;矩阵*矩阵&#xff09;范数取决于如何衡量b和c的长度常见范数矩阵范数&#xff1a;最小的满足的上面公式的值Frobenius范数特殊矩阵对称和反对称正定正交矩阵置换矩…

Solidity 中的数学(第 4 部分:复利)

本文是关于在 Solidity 中进行数学运算的系列文章中的第四篇。这次的主题是&#xff1a;复利。 介绍 在我们之前的文章中&#xff0c;我们讨论了百分比以及它们是如何在 Solidity 中计算的。在金融数学中&#xff0c;百分比通常与贷款和存款支付的利息有关。在每个时间段结束时…

深度学习入门基础CNN系列——批归一化(Batch Normalization)和丢弃法(dropout)

想要入门深度学习的小伙伴们&#xff0c;可以了解下本博主的其它基础内容&#xff1a; &#x1f3e0;我的个人主页 &#x1f680;深度学习入门基础CNN系列——卷积计算 &#x1f31f;深度学习入门基础CNN系列——填充&#xff08;padding&#xff09;与步幅&#xff08;stride&…