当前位置: 首页 > news >正文

郑州百度网站优化成熟网站开发联系电话

郑州百度网站优化,成熟网站开发联系电话,仙霞新村街道网站建设,网站建设到上线通讯协议之路主要分为两部分#xff0c;第一部分从理论上面讲解各类协议的通讯原理以及通讯格式#xff0c;第二部分从具体运用上讲解各类通讯协议的具体应用方法。 后续文章会同时发表在个人博客(jason1016.club)、CSDN#xff1b;视频会发布在bilibili(UID:399951374) 本文… 通讯协议之路主要分为两部分第一部分从理论上面讲解各类协议的通讯原理以及通讯格式第二部分从具体运用上讲解各类通讯协议的具体应用方法。 后续文章会同时发表在个人博客(jason1016.club)、CSDN视频会发布在bilibili(UID:399951374) 本文前缀 通讯协议专栏通讯协议_JASON丶LI的博客-CSDN博客 UART理论部分通讯协议学习之路UART协议理论-CSDN博客 具体实践方案选择 1、轮询模式 程序必须轮询状态位以检查是否已收到新字符并以足够快的速度读取它以获得所有字节 优点 很容易实现但在真正项目中的应用很少 缺点 在突发数据中很容易错过接收到的字符 仅适用于低波特率 应用程序必须非常快速地检查是否收到新的数据 2、中断模式 UART触发中断CPU跳转到服务程序处理数据接收 优点 目前程序中最常用的方法 在低速率下工作良好115200 波特 缺点 为每个接收到的字符执行中断服务程序 可能会在具有许多中断的高性能 MCU 中停止其他任务 一次接收突发数据时可能会停止操作系统 3、DMA模式 注意这里的DMA模式接收不定长数据时是检测IDLE空闲中断标志位来判断DMA接收是否完成的但是本人在使用proteus仿真中IDLE一直都不会挂起导致仿真的DMA接收无法实现后续会想方案解决 DMA 用于在硬件级别将数据从 USART RX 数据寄存器传输到用户存储器。 除了在必要时由应用程序处理接收到的数据此时不需要应用程序交互 优点 i.从 USART 外设到内存的传输是在硬件完成的无需 CPU干涉 ii.可以很容易地与操作系统一起工作 iii.针对最高波特率 1Mbps 和低功耗应用进行了优化 vi.在大量数据突发的情况下增加数据缓冲区大小可以改进功能 缺点 i.DMA 硬件必须事先知道要传输的字节数 ii.如果通信失败DMA 可能不会通知应用程序所有传输的字节 本文仅关注接收未知数据长度的 DMA 模式。 开发实践 对于usart的开发实践其实并没有学习理论时预想的那么负责因为目前市面上绝大部分单片机芯片内核都已经配备了完整的U(S)ART固件相当于厂家已经配置好了对应的协议传输方案我们要做的就是简单地配置一下其已存在的固件以及对应数据传输的规则即可。 本文以STM32F103C8为例分两种方式进行usart协议通讯的配置分别给标准库用户和HAL库用户详细的配置解决方案ps:寄存器开发没有作者寄存器开发不太熟练... 一、标准库 中断模式 单片机知识巩固 流控【STM32学习笔记】USART 硬件流控 - 知乎 (zhihu.com) usart协议的使用核心在与配置与数据的收发处理对于标准库而言USART的配置核心在与IO口的配置而数据的收发核心在于数据的发送格式和接收缓存标注位接收数据筛选。 1、UART的配置 USART协议硬件通道在单片机的配置本人有自己的一点想法。之前在理论中提到了单片机拟人化的概念在这章我将继续延续这个概念进行介绍。先让我们回忆一下之前提到晶振就是单片机的心跳信号每实现一次晶振的跳变信号单片机就执行一次指令周期而各类的总线就是血管和供血各类的IO口和IO协议就是单片机感知世界与世界交流的感官。 不知道大家有没有听过捕食者效应那就是当你饥饿的时候你会发现你的感官会变得更加灵敏比如我考试之前一般都不吃饭的这是什么原因呢原因就是当我们空腹时我们的消化系统就不需要工作血液的占用就少了但是血液的总量是不变的因此心脏每一次泵血血液就可以流向人身体内更需要他的地方。 回归单片机正常单片机这么多IO口当他每一个都开启工作的时候是不是就得持续给他们供电以及随时随地管理信号呀这会导致单片机的能耗变得非常高那怎么解决呢因此单片机所有的外设默认都是失能的什么意思呢就像刚刚提到的空腹状态就是单片机不需要消化这个外设通讯因此我们就可以失能消化系统这类的外设时钟就是不给他供血了。那当我们发现我们需要消化食物了那我们就重新开启这个外设的时钟就行了。这种按需开启有利于单片机大大节省能耗以及降低CPU占用率通常我们都是用啥就开啥其他的就不管了。 而单片机GPIO、USART、SPI、NVIC等这类型外设的配置以及各类的配置选项就需要大家各自去掌握了这里附上STM32F103的库函数编程文档供参考。 配置阶段分为 使能GPIO时钟使能USART时钟配置GPIO口配置USART口开启USART中断模式配置NVIC中断模式使能usart 具体配置方案以及配置原因参考下列代码。 void USART1_Init(u32 bound) //USART1初始化函数 {RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //使能USART1ckPB8,TXPA9,RXPA10RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOAGPIO_InitTypeDef GPIO_Initstructure; //定义GPIO结构体GPIO_Initstructure.GPIO_Mode GPIO_Mode_AF_PP; //设置GPIO为复用推挽输出GPIO_Initstructure.GPIO_Pin GPIO_Pin_9; //设置为Pin9GPIO_Initstructure.GPIO_Speed GPIO_Speed_50MHz; //设置为50MHZ速度GPIO_Init(GPIOA, GPIO_Initstructure); //按照上述结构体配置初始化GPIOAGPIO_Initstructure.GPIO_Mode GPIO_Mode_IPU; //设置GPIO为浮空输入GPIO_Initstructure.GPIO_Pin GPIO_Pin_10; //设置为Pin9GPIO_Initstructure.GPIO_Speed GPIO_Speed_50MHz; //设置为50MHZ速度GPIO_Init(GPIOA, GPIO_Initstructure); //按照上述结构体配置初始化GPIOAUSART_InitTypeDef USART_Initstructure; //定义UASRT结构体USART_Initstructure.USART_BaudRate bound; //配置波特率USART_Initstructure.USART_HardwareFlowControl USART_HardwareFlowControl_None; //配置流控USART_Initstructure.USART_Mode USART_Mode_Tx | USART_Mode_Rx; //配置通讯方式一般选择双控USART_Initstructure.USART_Parity USART_Parity_No; //配置是否需要校验位需要对应下面的数据长度WordLengthUSART_Initstructure.USART_StopBits USART_StopBits_1; //配置停止位USART_Initstructure.USART_WordLength USART_WordLength_8b; //配置数据长度可选8位和9位若设置校验位则配置9位否则配置8位即可USART_Init(USART1, USART_Initstructure); //按照上述结构体配置初始化USART1USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //使能USART中断NVIC_InitTypeDef NVIC_Initstructure; //定义NVIC结构体NVIC_Initstructure.NVIC_IRQChannel USART1_IRQn; //设置NVIC管理USART1中断NVIC_Initstructure.NVIC_IRQChannelCmd ENABLE; //确定使能NVIC_Initstructure.NVIC_IRQChannelPreemptionPriority 1; //设置抢占优先级为0NVIC_Initstructure.NVIC_IRQChannelSubPriority 1; //设置等候优先级为0NVIC_Init(NVIC_Initstructure); //按照上述结构体配置初始化NVICUSART_Cmd(USART1, ENABLE); //使能UASRT1 } 库函数配置关键点 param USART_FLAG:指定要检查的标志。arg USART_FLAG_CTS: CTS更改标志(不适用于UART4和UART5)arg USART_FLAG_LBD: LIN中断检测标志arg USART_FLAG_TXE:传输数据寄存器空标志arg USART_FLAG_TC:传输完成标志arg USART_FLAG_RXNE:接收数据寄存器不空标志arg USART_FLAG_IDLE:空闲线检测标志arg USART_FLAG_ORE:超限错误标志arg USART_FLAG_NE:噪声错误标志arg USART_FLAG_FE:帧错误标志arg USART_FLAG_PE:奇偶校验错误标志 param USART_IT:指定USART中断源要启用或禁用。arg USART_IT_CTS: CTS更改中断(不适用于UART4和UART5)arg USART_IT_LBD: LIN中断检测中断arg USART_IT_TXE:传输数据寄存器空中断arg USART_IT_TC:传输完成中断arg USART_IT_RXNE:接收数据寄存器不空中断arg USART_IT_IDLE:空闲线检测中断arg USART_IT_PE:奇偶校验错误中断arg USART_IT_ERR:中断错误(帧错误噪声错误溢出错误) 2、UART的发送 USART发送发本质就是调用USART_SendData()函数发送信息各类的信息发送都是基于该函数变形而得的。 void Serial_SendByte(uint8_t Byte) //编写发送函数 {USART_SendData(USART1, Byte); //发送字节函数while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) RESET); //检验发送是否完成 }//发送一个数组 void Serial_SendArray(uint8_t *Array, uint16_t Length) {uint16_t i;for (i 0; i Length; i ){Serial_SendByte(Array[i]);} }//发送一个字符串 void Serial_SendString(char *String) {uint8_t i;for (i 0; String[i] ! 0; i){Serial_SendByte(String[i]);} }//取X的Y次方用于下列运算 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 (i 0; i Length; i ){Serial_SendByte(Number/Serial_Pow(10, Length - i - 1) % 10 0);} }//重写fputc函数 int fputc(int ch, FILE *f) {Serial_SendByte(ch);return ch; }//封装printf可变参数格式 void Serial_Printf(char *format, ...) {char String[100];va_list arg;va_start(arg, format);vsprintf(String, format, arg);va_end(arg);Serial_SendString(String); } 3、UART的接收 UART数据接收的本质 当已使能的UART信道接收到信息的时候触发usart中断, 然后在中断事件中调用USART_GetITStatus()函数检查接收数据寄存器是否为空 若检查到数据则将数据转移到Serial_RxData中进行数据的存储,同时挂起标志位Serial_RxFlag,方便后续要轮询模式中进行数据的打印或调用操作. 最后调用USART_ClearITPendingBit()函数进行中断挂起状态的清除,退出中断并方便下次再次进入中断. uint8_t Serial_RxData; //定义UASRT1_RX数据缓存 uint8_t Serial_RxFlag; //定义USART1_RX接收标志位用于后续TX特定数据的接收 char Serial_RxPacket[100];//调用标志位和重置标志位 uint8_t Serial_GetFlag(void) {if (Serial_RxFlag 1){Serial_RxFlag 0;return 1;}return 0; }uint8_t Serial_GetRxData(void) {return Serial_RxData; }//无限制接收//void USART1_IRQHandler(void) //{ // if (USART_GetITStatus(USART1, USART_IT_RXNE) SET) // { // Serial_RxData USART_ReceiveData(USART1); // Serial_RxFlag 1; // USART_ClearITPendingBit(USART1, USART_IT_RXNE); // } //}//接收筛选接收数据包 void USART1_IRQHandler(void) //编写USART1中断函数处理接收事件 {//static为静态变量只需定义一次只能再规定函数中执行的全局变量static uint8_t RxStare 0;static uint8_t pRxPacket 0;if (USART_GetITStatus(USART1, USART_IT_RXNE) SET){uint8_t RxData USART_ReceiveData(USART1);if (RxStare 0) //状态1{if (RxData Serial_RxFlag 0){RxStare 1;pRxPacket 0;}}else if (RxStare 1) //状态2{if (RxData \r){RxStare 2;}else{Serial_RxPacket[pRxPacket] RxData;pRxPacket ;}}else if (RxStare 2) //状态3{if (RxData \n){RxStare 0;Serial_RxPacket[pRxPacket] \0;Serial_RxFlag 1;}}USART_ClearITPendingBit(USART1, USART_FLAG_RXNE);} }DMA模式: 参考文档 STM32 DMA串口发送模式配置及使用简单分享 - 知乎 (zhihu.com) STM32 | 串口DMA很难其实就是如此简单超详细、附代码-CSDN博客 对于DMA转运核心就在于DMA通道的配置与选取发送不需要用到cpu接收需要进入cpu的中断模式进行数据的处理 对于DMA的发送核心在于调用DMA_Cmd(DMA1_Channel7, ENABLE);函数将原先设定好的CNDTR数据长度的CMAR数据地址数据发送出去 对于DMA的接收分为定长与不定长两种接收方式 定长方式采用DMA1_Channel6_IRQHandler中断方式当接收数据缓存满了之后就会发生中断将数据缓存内的所有数据读取。 不定长方式采用USART2_IRQHandlerIDLE方式的中断方式接收串口空闲标志位当数据接收完存入数据缓存之后就会触发IDLE中断这是我们将读取数据缓存内的数据同时检查数据缓存内的剩余容量这样数据长度就等于数据缓存总容量-数据缓存剩余容量当得知数据长度与数据地址后就可以实现不定长数据的接收了。 配置 #include usart2.h//USART2_MAX_TX_LEN和USART2_MAX_RX_LEN在头文件进行了宏定义分别指USART2最大发送长度和最大接收长度 u8 USART2_TX_BUF[USART2_MAX_TX_LEN]; //发送缓冲,最大USART2_MAX_TX_LEN字节 u8 u1rxbuf[USART2_MAX_RX_LEN]; //发送数据缓冲区1 u8 u2rxbuf[USART2_MAX_RX_LEN]; //发送数据缓冲区2 u8 witchbuf0; //标记当前使用的是哪个缓冲区,0使用u1rxbuf1使用u2rxbuf u8 USART2_TX_FLAG0; //USART2发送标志启动发送时置1 u8 USART2_RX_FLAG0; //USART2接收标志启动接收时置1void Initial_UART2(unsigned long baudrate) {//GPIO端口设置GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2 | RCC_APB2Periph_GPIOA, ENABLE); //使能USART2GPIOA时钟//USART2_TX GPIOA.2初始化GPIO_InitStructure.GPIO_Pin GPIO_Pin_2; //PA.2GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; //复用推挽输出GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; //GPIO速率50MHzGPIO_Init(GPIOA, GPIO_InitStructure); //初始化GPIOA.2//USART2_RX GPIOA.3初始化GPIO_InitStructure.GPIO_Pin GPIO_Pin_3; //PA.3GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; //浮空输入GPIO_Init(GPIOA, GPIO_InitStructure); //初始化GPIOA.3//USART 初始化设置USART_InitStructure.USART_BaudRate baudrate; //串口波特率USART_InitStructure.USART_WordLength USART_WordLength_8b; //字长为8位数据格式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_Rx | USART_Mode_Tx; //收发模式USART_Init(USART2, USART_InitStructure); //初始化串口2//中断开启设置USART_ITConfig(USART2, USART_IT_IDLE, ENABLE); //开启检测串口空闲状态中断USART_ClearFlag(USART2,USART_FLAG_TC); //清除USART2标志位USART_Cmd(USART2, ENABLE); //使能串口2NVIC_InitStructure.NVIC_IRQChannel USART2_IRQn; //NVIC通道设置NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 8; //抢占优先级8NVIC_InitStructure.NVIC_IRQChannelSubPriority 0; //响应优先级NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; //IRQ通道使能NVIC_Init(NVIC_InitStructure); //根据指定的参数初始化NVIC寄存器DMA1_USART2_Init(); //DMA1_USART2初始化 }void DMA1_USART2_Init(void) {DMA_InitTypeDef DMA1_Init;NVIC_InitTypeDef NVIC_InitStructure;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); //使能DMA1时钟//DMA_USART2_RX USART2-RAM的数据传输DMA_DeInit(DMA1_Channel6); //将DMA的通道6寄存器重设为缺省值 DMA1_Init.DMA_PeripheralBaseAddr (u32)(USART2-DR); //启动传输前装入实际RAM地址DMA1_Init.DMA_MemoryBaseAddr (u32)u1rxbuf; //设置接收缓冲区首地址DMA1_Init.DMA_DIR DMA_DIR_PeripheralSRC; //数据传输方向从外设读取到内存DMA1_Init.DMA_BufferSize USART2_MAX_RX_LEN; //DMA通道的DMA缓存的大小DMA1_Init.DMA_PeripheralInc DMA_PeripheralInc_Disable; //外设地址寄存器不变DMA1_Init.DMA_MemoryInc DMA_MemoryInc_Enable; //内存地址寄存器递增DMA1_Init.DMA_PeripheralDataSize DMA_PeripheralDataSize_Byte; //数据宽度为8位DMA1_Init.DMA_MemoryDataSize DMA_MemoryDataSize_Byte; //数据宽度为8位DMA1_Init.DMA_Mode DMA_Mode_Normal; //工作在正常模式DMA1_Init.DMA_Priority DMA_Priority_High; //DMA通道 x拥有高优先级 DMA1_Init.DMA_M2M DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输DMA_Init(DMA1_Channel6,DMA1_Init); //对DMA通道6进行初始化//DMA_USART2_TX RAM-USART2的数据传输DMA_DeInit(DMA1_Channel7); //将DMA的通道7寄存器重设为缺省值 DMA1_Init.DMA_PeripheralBaseAddr (u32)(USART2-DR); //启动传输前装入实际RAM地址DMA1_Init.DMA_MemoryBaseAddr (u32)USART2_TX_BUF; //设置发送缓冲区首地址DMA1_Init.DMA_DIR DMA_DIR_PeripheralDST; //数据传输方向从内存发送到外设DMA1_Init.DMA_BufferSize USART2_MAX_TX_LEN; //DMA通道的DMA缓存的大小DMA1_Init.DMA_PeripheralInc DMA_PeripheralInc_Disable; //外设地址寄存器不变DMA1_Init.DMA_MemoryInc DMA_MemoryInc_Enable; //内存地址寄存器递增DMA1_Init.DMA_PeripheralDataSize DMA_PeripheralDataSize_Byte; //数据宽度为8位DMA1_Init.DMA_MemoryDataSize DMA_MemoryDataSize_Byte; //数据宽度为8位DMA1_Init.DMA_Mode DMA_Mode_Normal; //工作在正常模式DMA1_Init.DMA_Priority DMA_Priority_High; //DMA通道 x拥有高优先级 DMA1_Init.DMA_M2M DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输DMA_Init(DMA1_Channel7,DMA1_Init); //对DMA通道7进行初始化//DMA1通道6 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel DMA1_Channel6_IRQn; //NVIC通道设置NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 3 ; //抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority 0; //子优先级NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; //IRQ通道使能NVIC_Init(NVIC_InitStructure); //根据指定的参数初始化NVIC寄存器//DMA1通道7 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel DMA1_Channel7_IRQn; //NVIC通道设置NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 3 ; //抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; //子优先级NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; //IRQ通道使能NVIC_Init(NVIC_InitStructure); //根据指定的参数初始化NVIC寄存器DMA_ITConfig(DMA1_Channel6,DMA_IT_TC,ENABLE); //开USART2 Rx DMA中断DMA_ITConfig(DMA1_Channel7,DMA_IT_TC,ENABLE); //开USART2 Tx DMA中断DMA_Cmd(DMA1_Channel6,ENABLE); //使DMA通道6停止工作DMA_Cmd(DMA1_Channel7,DISABLE); //使DMA通道7停止工作USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE); //开启串口DMA发送USART_DMACmd(USART2, USART_DMAReq_Rx, ENABLE); //开启串口DMA接收 } 发送函数 //DMA 发送应用源码 void DMA_USART2_Tx_Data(u8 *buffer, u32 size) {while(USART2_TX_FLAG); //等待上一次发送完成USART2_TX_FLAG为1即还在发送数据USART2_TX_FLAG1; //USART2发送标志启动发送DMA1_Channel7-CMAR (uint32_t)buffer; //设置要发送的数据地址DMA1_Channel7-CNDTR size; //设置要发送的字节数目DMA_Cmd(DMA1_Channel7, ENABLE); //开始DMA发送 }void USART2_printf(char *format, ...) {//VA_LIST 是在C语言中解决变参问题的一组宏所在头文件#include stdarg.h用于获取不确定个数的参数。va_list arg_ptr; //实例化可变长参数列表while(USART2_TX_FLAG); //等待上一次发送完成USART2_TX_FLAG为1即还在发送数据va_start(arg_ptr, format); //初始化可变参数列表设置format为可变长列表的起始点第一个元素// USART2_MAX_TX_LEN1可接受的最大字符数(非字节数UNICODE一个字符两个字节), 防止产生数组越界vsnprintf((char*)USART2_TX_BUF, USART2_MAX_TX_LEN1, format, arg_ptr); //从USART2_TX_BUF的首地址开始拼合拼合format内容USART2_MAX_TX_LEN1限制长度防止产生数组越界va_end(arg_ptr); //注意必须关闭DMA_USART2_Tx_Data(USART2_TX_BUF,strlen((const char*)USART2_TX_BUF)); //发送USART2_TX_BUF内容 } 接收函数 //处理DMA1 通道6的接收完成中断 void DMA1_Channel6_IRQHandler(void) {u8 *p;if(DMA_GetITStatus(DMA1_IT_TC6)! RESET) //DMA接收完成标志{DMA_ClearITPendingBit(DMA1_IT_TC6); //清除中断标志 USART_ClearFlag(USART2,USART_FLAG_TC); //清除USART2标志位DMA_Cmd(DMA1_Channel6, DISABLE ); //关闭USART2 TX DMA1 所指示的通道if(witchbuf) //之前用的u2rxbuf切换为u1rxbuf{pu2rxbuf; //先保存前一次数据地址再切换缓冲区DMA1_Channel6-CMAR(u32)u1rxbuf; //切换为u1rxbuf缓冲区地址witchbuf0; //下一次切换为u2rxbuf}else //之前用的u1rxbuf切换为u2rxbuf{pu1rxbuf; //先保存前一次数据地址再切换缓冲区DMA1_Channel6-CMAR(u32)u2rxbuf; //切换为u2rxbuf缓冲区地址witchbuf1; //下一次切换为u1rxbuf}DMA1_Channel6-CNDTR USART2_MAX_RX_LEN; //DMA通道的DMA缓存的大小DMA_Cmd(DMA1_Channel6, ENABLE); //使能USART2 TX DMA1 所指示的通道//******************↓↓↓↓↓这里作数据处理↓↓↓↓↓******************//DMA_USART2_Tx_Data(p,USART2_MAX_RX_LEN);//******************↑↑↑↑↑这里作数据处理↑↑↑↑↑******************//} }//DMA1通道7中断 void DMA1_Channel7_IRQHandler(void) {if(DMA_GetITStatus(DMA1_IT_TC7)! RESET) //DMA接收完成标志{DMA_ClearITPendingBit(DMA1_IT_TC7); //清除中断标志 USART_ClearFlag(USART2,USART_FLAG_TC); //清除串口2的标志位DMA_Cmd(DMA1_Channel7, DISABLE ); //关闭USART2 TX DMA1 所指示的通道USART2_TX_FLAG0; //USART2发送标志(关闭)} }//串口2中断函数 void USART2_IRQHandler(void) {u8 *p;u8 USART2_RX_LEN 0; //接收数据长度if(USART_GetITStatus(USART2, USART_IT_IDLE) ! RESET) //串口2空闲中断{USART_ReceiveData(USART2); //清除串口2空闲中断IDLE标志位USART_ClearFlag(USART2,USART_FLAG_TC); //清除USART2标志位DMA_Cmd(DMA1_Channel6, DISABLE ); //关闭USART2 TX DMA1 所指示的通道USART2_RX_LEN USART2_MAX_RX_LEN - DMA1_Channel6-CNDTR; //获得接收到的字节数if(witchbuf) //之前用的u2rxbuf切换为u1rxbuf{pu2rxbuf; //先保存前一次数据地址再切换缓冲区DMA1_Channel6-CMAR(u32)u1rxbuf; //切换为u1rxbuf缓冲区地址witchbuf0; //下一次切换为u2rxbuf}else //之前用的u1rxbuf切换为u2rxbuf{pu1rxbuf; //先保存前一次数据地址再切换缓冲区DMA1_Channel6-CMAR(u32)u2rxbuf; //切换为u2rxbuf缓冲区地址witchbuf1; //下一次切换为u1rxbuf}DMA1_Channel6-CNDTR USART2_MAX_RX_LEN; //DMA通道的DMA缓存的大小DMA_Cmd(DMA1_Channel6, ENABLE); //使能USART2 TX DMA1 所指示的通道//******************↓↓↓↓↓这里作数据处理↓↓↓↓↓******************//DMA_USART2_Tx_Data(p,USART2_RX_LEN);//******************↑↑↑↑↑这里作数据处理↑↑↑↑↑******************//}USART_ClearITPendingBit(USART2,USART_IT_ORE); //清除USART2_ORE标志位 } 二、HAL库 这里先放上HAL库的串口句柄 typedef struct __UART_HandleTypeDef {USART_TypeDef *Instance; /*! UART registers base address */UART_InitTypeDef Init; /*! UART communication parameters */uint8_t *pTxBuffPtr; /*! Pointer to UART Tx transfer Buffer */uint16_t TxXferSize; /*! UART Tx Transfer size */__IO uint16_t TxXferCount; /*! UART Tx Transfer Counter */uint8_t *pRxBuffPtr; /*! Pointer to UART Rx transfer Buffer */uint16_t RxXferSize; /*! UART Rx Transfer size */__IO uint16_t RxXferCount; /*! UART Rx Transfer Counter */__IO HAL_UART_RxTypeTypeDef ReceptionType; /*! Type of ongoing reception */DMA_HandleTypeDef *hdmatx; /*! UART Tx DMA Handle parameters */DMA_HandleTypeDef *hdmarx; /*! UART Rx DMA Handle parameters */HAL_LockTypeDef Lock; /*! Locking object */__IO HAL_UART_StateTypeDef gState; /*! UART state information related to global Handle managementand also related to Tx operations.This parameter can be a value of ref HAL_UART_StateTypeDef */__IO HAL_UART_StateTypeDef RxState; /*! UART state information related to Rx operations.This parameter can be a value of ref HAL_UART_StateTypeDef */__IO uint32_t ErrorCode; /*! UART Error code */#if (USE_HAL_UART_REGISTER_CALLBACKS 1)void (* TxHalfCpltCallback)(struct __UART_HandleTypeDef *huart); /*! UART Tx Half Complete Callback */void (* TxCpltCallback)(struct __UART_HandleTypeDef *huart); /*! UART Tx Complete Callback */void (* RxHalfCpltCallback)(struct __UART_HandleTypeDef *huart); /*! UART Rx Half Complete Callback */void (* RxCpltCallback)(struct __UART_HandleTypeDef *huart); /*! UART Rx Complete Callback */void (* ErrorCallback)(struct __UART_HandleTypeDef *huart); /*! UART Error Callback */void (* AbortCpltCallback)(struct __UART_HandleTypeDef *huart); /*! UART Abort Complete Callback */void (* AbortTransmitCpltCallback)(struct __UART_HandleTypeDef *huart); /*! UART Abort Transmit Complete Callback */void (* AbortReceiveCpltCallback)(struct __UART_HandleTypeDef *huart); /*! UART Abort Receive Complete Callback */void (* WakeupCallback)(struct __UART_HandleTypeDef *huart); /*! UART Wakeup Callback */void (* RxEventCallback)(struct __UART_HandleTypeDef *huart, uint16_t Pos); /*! UART Reception Event Callback */void (* MspInitCallback)(struct __UART_HandleTypeDef *huart); /*! UART Msp Init callback */void (* MspDeInitCallback)(struct __UART_HandleTypeDef *huart); /*! UART Msp DeInit callback */ #endif /* USE_HAL_UART_REGISTER_CALLBACKS */} UART_HandleTypeDef; 1、串口发送/接收函数 HAL_UART_Transmit()串口发送数据使用超时管理机制 HAL_UART_Receive()串口接收数据使用超时管理机制 HAL_UART_Transmit_IT()串口中断模式发送 HAL_UART_Receive_IT()串口中断模式接收 HAL_UART_Transmit_DMA()串口DMA模式发送 HAL_UART_Transmit_DMA()串口DMA模式接收串口发送数据 HAL_UART_Transmit HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout) 功能串口发送指定长度的数据。如果超时没发送完成则不再发送返回超时标志HAL_TIMEOUT。 参数 UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1*pData 需要发送的数据Size 发送的字节数Timeout 最大发送时间发送数据超过该时间退出发送 举例 HAL_UART_Transmit(huart1, (uint8_t *)ZZX, 3, 0xffff); //串口发送三个字节数据最大传输时间0xffff 中断接收数据 HAL_UART_Receive_IT HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) 功能串口中断接收以中断方式接收指定长度数据。 大致过程是设置数据存放位置接收数据长度然后使能串口接收中断。接收到数据时会触发串口中断。 再然后串口中断函数处理直到接收到指定长度数据而后关闭中断进入中断接收回调函数不再触发接收中断。(只触发一次中断) 参数 UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1 *pData 接收到的数据存放地址 Size 接收的字节数 举例 HAL_UART_Receive_IT(huart1,(uint8_t *)value,1); //中断接收一个字符存储到value中 2、串口中断函数 HAL_UART_IRQHandler(UART_HandleTypeDef *huart); //串口中断处理函数 HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart); //串口发送中断回调函数 HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart); //串口发送一半中断回调函数用的较少 HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); //串口接收中断回调函数 HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);//串口接收一半回调函数用的较少 HAL_UART_ErrorCallback();串口接收错误函数 a.串口中断服务函数 USART1_IRQHandler(void)【不需要配置】 USART1_IRQHandler(void) 功能当我们使能了中断并且中断发生时就会执行这里的中断服务函数。 这个函数在MX配置后会自行进行下面所说的一系列判断不需要额外配置因此HAL库不同于标准库标准库是在这个函数里面进行响应的中断处理事件的配置而标准库则在这里面进行接收和发送两个事件类型的判断再跳转到对应的函数进行对应的处理。 b.串口中断处理函数 HAL_UART_IRQHandler(UART_HandleTypeDef *huart)【不需要配置】 HAL_UART_IRQHandler(UART_HandleTypeDef *huart); 功能对接收到的数据进行判断和处理 判断是发送中断还是接收中断然后进行数据的发送和接收在中断服务函数中使用 如果接收数据则会进行接收中断处理函数 /* UART in mode Receiver ---------------------------------------------------*/ if((tmp_flag ! RESET) (tmp_it_source ! RESET)) { UART_Receive_IT(huart); } 如果发送数据则会进行发送中断处理函数 /* UART in mode Transmitter ------------------------------------------------*/ if (((isrflags USART_SR_TXE) ! RESET) ((cr1its USART_CR1_TXEIE) ! RESET)) { UART_Transmit_IT(huart); return; } c.❤️串口接收中断回调函数 HAL_UART_RxCpltCallback(huart)【需要配置】 HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); 功能HAL库的中断进行完之后并不会直接退出而是会进入中断回调函数中用户可以在其中设置代码,串口中断接收完成之后会进入该函数该函数为空函数用户需自行修改 参数 UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1 举例 HAL_UART_RxCpltCallback(huart1){ //用户设定的代码 } 3、串口查询函数 HAL_UART_GetState();  判断UART的接收是否结束或者发送数据是否忙碌 举例      while(HAL_UART_GetState(huart4) HAL_UART_STATE_BUSY_TX) //检测UART发送结束 三、 HAL库具体实现过程 1、发送函数重定向 引入printf重定向代码块 代码最适合加在CubeMX自动生成后的usart.c文件的 / * USER CODE BEGIN 0 * / 和 / * USER CODE END 0 * / 中间 /* USER CODE BEGIN 0 */ #include stdio.h#ifdef __GNUC__#define PUTCHAR_PROTOTYPE int _io_putchar(int ch)#else#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)#endif /* __GNUC__*//*******************************************************************brief Retargets the C library printf function to the USART.*param None*retval None******************************************************************/PUTCHAR_PROTOTYPE{HAL_UART_Transmit(huart1, (uint8_t *)ch,1,0xFFFF);return ch;} /* USER CODE END 0 */添加#includestdio.h 比较全局的办法就是将#include直接加入main.h中因为Cube生成文件大部分都是包含了main.h的所以除了自建文件几乎都可以全局包含到stdio.h而且自建文件也可以直接包含main.h我的习惯是把工程用的共性的概率高的头文件都放在main.h里面具体位置如下 //main.h/* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #includestdio.h /* USER CODE END Includes */在自建文件使用printf函数时记得#include main.c内测试的代码 while (1){/* USER CODE END WHILE */printf(串口打印测试\n);HAL_Delay(1000);/* USER CODE BEGIN 3 */}注意使用此代码时还要在魔术棒那个选项中打勾“UseMicroLIB”否则stdio.h是编译不了的但它又不会报错。 参考文章STM32-HAL库-printf函数重定向USART应用实例_hal库printf重定向_Calvin Haynes的博客-CSDN博客 2、⭐接收函数(中断) 因为中断接收函数只能触发一次接收中断所以我们需要在中断回调函数中再调用一次中断接收函数 具体流程 1、初始化串口 2、在main中第一次调用接收中断函数 3、进入接收中断接收完数据 进入中断回调函数 4、修改HAL_UART_RxCpltCallback中断回调函数处理接收的数据 5 、回调函数中要调用一次HAL_UART_Receive_IT函数使得程序可以重新触发接收中断 函数流程图 HAL_UART_Receive_IT(中断接收函数) - USART2_IRQHandler(void)(中断服务函数) - HAL_UART_IRQHandler(UART_HandleTypeDef *huart)(中断处理函数) - UART_Receive_IT(UART_HandleTypeDef *huart) (接收函数) - HAL_UART_RxCpltCallback(huart);(中断回调函数) HAL_UART_RxCpltCallback函数就是用户要重写在main.c里的回调函数。 代码实现 在main.c中添加下列定义 #include string.h#define RXBUFFERSIZE 256 //最大接收字节数 char RxBuffer[RXBUFFERSIZE]; //接收数据 uint8_t aRxBuffer; //接收中断缓冲 uint8_t Uart1_Rx_Cnt 0; //接收缓冲计数 在main()主函数中调用一次接收中断函数 /* USER CODE BEGIN 2 */HAL_UART_Receive_IT(huart1, (uint8_t *)aRxBuffer, 1); /* USER CODE END 2 */ 在main.c下方添加中断回调函数 /* USER CODE BEGIN 4 */void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {/* Prevent unused argument(s) compilation warning */UNUSED(huart);/* NOTE: This function Should not be modified, when the callback is needed,the HAL_UART_TxCpltCallback could be implemented in the user file*/if(Uart1_Rx_Cnt 255) //溢出判断{Uart1_Rx_Cnt 0;memset(RxBuffer,0x00,sizeof(RxBuffer));HAL_UART_Transmit(huart1, (uint8_t *)数据溢出, 10,0xFFFF); }else{RxBuffer[Uart1_Rx_Cnt] aRxBuffer; //接收数据转存if((RxBuffer[Uart1_Rx_Cnt-1] 0x0A)(RxBuffer[Uart1_Rx_Cnt-2] 0x0D)) //判断结束位{HAL_UART_Transmit(huart1, (uint8_t *)RxBuffer, Uart1_Rx_Cnt,0xFFFF); //将收到的信息发送出去while(HAL_UART_GetState(huart1) HAL_UART_STATE_BUSY_TX);//检测UART发送结束Uart1_Rx_Cnt 0;memset(RxBuffer,0x00,sizeof(RxBuffer)); //清空数组}}HAL_UART_Receive_IT(huart1, (uint8_t *)aRxBuffer, 1); //再开启接收中断 } /* USER CODE END 4 */ 参考文章【STM32】HAL库 STM32CubeMX教程四---UART串口通信详解_hal_uart_transmit-CSDN博客 3、发送函数DMA转运 UART DMA函数库 HAL_UART_Transmit();串口发送数据使用超时管理机制 HAL_UART_Receive();串口接收数据使用超时管理机制 HAL_UART_Transmit_IT();串口中断模式发送 HAL_UART_Receive_IT();串口中断模式接收 HAL_UART_Transmit_DMA();串口DMA模式发送 HAL_UART_Transmit_DMA();串口DMA模式接收 HAL_UART_DMAPause() 暂停串口DMA HAL_UART_DMAResume(); 恢复串口DMA HAL_UART_DMAStop(); 结束串口DMA在main.C中添加 /* USER CODE BEGIN Init / uint8_t Senbuff[] \r\n*** Serial Output Message by DMA **\r\n UART DMA Test \r\n Zxiaoxuan; //定义数据发送数组 / USER CODE END Init */ while循环 while (1) { /* USER CODE END WHILE */ HAL_UART_Transmit_DMA(huart1, (uint8_t )Senbuff, sizeof(Senbuff));HAL_Delay(1000);/ USER CODE BEGIN 3 */ } 参考文章【STM32】HAL库 STM32CubeMX教程十一---DMA (串口DMA发送接收)_hal库dma串口接收-CSDN博客 4、接收函数DMA STM32的IDLE的中断产生条件在串口无数据接收的情况下不会产生当清除IDLE标志位后必须有接收到第一个数据后才开始触发一但接收的数据断流没有接收到数据即产生IDLE中断 使用DMA串口接受空闲中断 实现将接收的数据完整发送到上位机的功能 uart.c volatile uint8_t rx_len 0; //接收一帧数据的长度 volatile uint8_t recv_end_flag 0; //一帧数据接收完成标志 uint8_t rx_buffer[100]{0}; //接收数据缓存数组void MX_USART1_UART_Init(void) {huart1.Instance USART1;huart1.Init.BaudRate 115200;huart1.Init.WordLength UART_WORDLENGTH_8B;huart1.Init.StopBits UART_STOPBITS_1;huart1.Init.Parity UART_PARITY_NONE;huart1.Init.Mode UART_MODE_TX_RX;huart1.Init.HwFlowCtl UART_HWCONTROL_NONE;huart1.Init.OverSampling UART_OVERSAMPLING_16;if (HAL_UART_Init(huart1) ! HAL_OK){Error_Handler();} //下方为自己添加的代码__HAL_UART_ENABLE_IT(huart1, UART_IT_IDLE); //使能IDLE中断//DMA接收函数此句一定要加不加接收不到第一次传进来的实数据是空的且此时接收到的数据长度为缓存器的数据长度HAL_UART_Receive_DMA(huart1,rx_buffer,BUFFER_SIZE);}uart.h extern UART_HandleTypeDef huart1; extern DMA_HandleTypeDef hdma_usart1_rx; extern DMA_HandleTypeDef hdma_usart1_tx; /* USER CODE BEGIN Private defines */#define BUFFER_SIZE 100 extern volatile uint8_t rx_len ; //接收一帧数据的长度 extern volatile uint8_t recv_end_flag; //一帧数据接收完成标志 extern uint8_t rx_buffer[100]; //接收数据缓存数组main.c /* ********************************************************************************************************* * 函 数 名: DMA_Usart_Send * 功能说明: 串口发送功能函数 * 形 参: buflen * 返 回 值: 无 ********************************************************************************************************* */ void DMA_Usart_Send(uint8_t *buf,uint8_t len)//串口发送封装 {if(HAL_UART_Transmit_DMA(huart1, buf,len)! HAL_OK) //判断是否发送正常如果出现异常则进入异常中断函数{Error_Handler();}}/* ********************************************************************************************************* * 函 数 名: DMA_Usart1_Read * 功能说明: 串口接收功能函数 * 形 参: Data,len * 返 回 值: 无 ********************************************************************************************************* */ void DMA_Usart1_Read(uint8_t *Data,uint8_t len)//串口接收封装 {HAL_UART_Receive_DMA(huart1,Data,len);//重新打开DMA接收 }while循环 while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */if(recv_end_flag 1) //接收完成标志{DMA_Usart_Send(rx_buffer, rx_len);rx_len 0;//清除计数recv_end_flag 0;//清除接收结束标志位 // for(uint8_t i0;irx_len;i) // { // rx_buffer[i]0;//清接收缓存 // }memset(rx_buffer,0,rx_len);}HAL_UART_Receive_DMA(huart1,rx_buffer,BUFFER_SIZE);//重新打开DMA接收 }main.c中的 HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {/* Prevent unused argument(s) compilation warning */UNUSED(huart);/* NOTE: This function Should not be modified, when the callback is needed,the HAL_UART_TxCpltCallback could be implemented in the user file*/if(huart huart2)//目前用不了IDLE标志位在Proteus中不知道为什么无法检测{uint32_t tmp_flag 0;uint32_t temp;tmp_flag __HAL_UART_GET_FLAG(huart2,UART_FLAG_IDLE); //获取IDLE标志位if((tmp_flag ! RESET))//idle标志被置位{ LED_turn(GPIO_PIN_2);__HAL_UART_CLEAR_IDLEFLAG(huart2);//清除标志位//temp huart1.Instance-SR; //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能//temp huart1.Instance-DR; //读取数据寄存器中的数据//这两句和上面那句等效HAL_UART_DMAStop(huart2); // 停止DMA传输防止temp __HAL_DMA_GET_COUNTER(hdma_usart2_rx);// 获取DMA中未传输的数据个数 //temp hdma_usart1_rx.Instance-NDTR;// 读取NDTR寄存器获取DMA中未传输的数据个数rx_len BUFFER_SIZE - temp; //总计数减去未传输的数据个数得到已经接收的数据个数recv_end_flag 1; // 接受完成标志位置1 }}}
http://www.huolong8.cn/news/375478/

相关文章:

  • 做私活的网站58同城房产信息
  • 牡丹江商城网站开发设计2 网站内部链接优化
  • 高仿奢侈手表网站织梦做的网站怎么添加关键词
  • 用flash做网站建设青岛 正规网站空间
  • 专题网站建设的请示国内交互网站
  • 焦作网站制作-焦作网站建设-焦作网络公司-维科网络标志设计名词解释
  • 网站建设与运营的预算方案模板仙游网站建设
  • 最好的营销型网站建设公司最好的做网站的公司
  • 网站首页页面设计网站建设对于企业的重要性
  • 怎样做网站seo学校网站推广策划书
  • html5 珠宝网站网络系统管理员
  • 中山比好的做网站的公司wordpress 有赞收款
  • 企业网站seo优化交流wordpress不能启动怎么解决
  • 企业网站会员功能小说网站签约作者应该怎么做
  • 屏幕分辨率 网站开发网站建设与管理代码
  • 制作网站的收获体会网站设计公司 杭州
  • 十二冶金建设集团有限公司网站河南专业网站建设公司哪家好
  • 网站开发合同 下载温州网站建设科技有限公司
  • 做关键词搜索的网站美容培训东莞网站建设
  • wordpress 复合筛选重庆seo推广运营
  • ps做的网站稿怎么做成网站网站建设网络
  • 网站编辑模版网站建设公司地址在哪
  • 房地产网站做编辑刚刚入行做网站用php吗
  • 赣州人才网最新招聘信息2023年长沙网站排名优化
  • 新网站怎么快速收录必做山西网站建设
  • 南宁本地有几家网站开发整站seo需要多少钱
  • 招商网站建设全包设计建立企业网站最佳的公司
  • 太仓有没有做网站建设的网站制作公司业务发展方案
  • 自己建网站怎么做影视资源网站建设售后服务明细
  • o2o系统网站建设沈阳德泰诺网站制作