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

网站优化专家阿里云部署一个自己做的网站吗

网站优化专家,阿里云部署一个自己做的网站吗,浙江招标信息网,和林格尔网站制作目录 一、DMA基础知识 1、DMA简介 (1)DMA控制器 (2)DMA流 (3)DMA请求 (4)仲裁器 (5)DMA传输属性 2、源地址和目标地址 3、DMA传输模式 4、传输数据量的大小 5、数据宽度 6、地址指针递增 7、DMA工作模式 8、DMA流的优先级别 9、FIFO或直接模式 10、单次传输或突…目录 一、DMA基础知识 1、DMA简介 (1)DMA控制器 (2)DMA流 (3)DMA请求 (4)仲裁器 (5)DMA传输属性 2、源地址和目标地址 3、DMA传输模式 4、传输数据量的大小 5、数据宽度 6、地址指针递增 7、DMA工作模式 8、DMA流的优先级别 9、FIFO或直接模式 10、单次传输或突发传输 11、双缓冲区模式 二、DMA的HAL驱动 1、DMA的HAL函数 2、DMA传输初始化 3、启动DMA数据传输 4、DMA的中断 三、工程配置 1、设计目的和通讯协议 2、工程设置 1时钟 2DEBUG 3 RTC 4USART2 5NVIC 6Project Manager Code Generater 四、软件代码 1、main.c 2、usart.h 3、usart.c 4、rtc.c 五、运行与调试 1、合规的指令  2、proBuffer[0]不是#或proBuffer[4]不是 3、指令长度小于5 4、仅proBuffer[2]或proBuffer[3]不是数字 5、 ;位于proBuffer[2]或proBuffer[3]位置 6、proBuffer[2]和proBuffer[3]数字超范围 7、指令长度大于5 本文通过STM32G474RET6介绍DMA基础知识然后通过USART2以DMA方式从上位机接收指令数据、处理指令数据、增加程序的容错能力、最后向上位机发送RTCtime。 本文通过测试环节也发现了作者在前几篇利用串口中断接收、处理和发送RTCtime的文章里没有发现的、可能的错误处理方法与疏漏挖掘的不够深刻当指令长度小于5或大于5时只有在其后累计输入的字符长度恰好等于5的倍数时程序才会跳转到正常。否则即使输入长度不等于5的指令后接着输入正确的指令程序也逃不出出错的死循环。但是当错误的指令长度是5的倍数的时候比如指令长度是10直接或多次发送指令就能顺利地跳转到正常。 一、DMA基础知识 直接存储器访问(Direct Memory AccessDMA)是实现存储器与外设、存储器与存储之间高效数据传输的方法。DMA数据传输无须CPU操作是一种硬件化的高速数据传输减少CPU的负载。在需要进行大量或高速数据传输时DMA传输方式特别有用。 1、DMA简介 STM32G474RET6有两个DMA控制器即DMA1和DMA2。一个DMA控制器的框图如图  (1)DMA控制器 DMA控制器上图左侧蓝色区域是管理DMA的硬件资源实现DMA数据传输的控制器一个硬件模块。MCU上有2个DMA控制器即DMA1和DMA2。这两个DMA控制器的本结构和功能相同STM32G474的两个DMAs支持 ● Memory-to-memory transfer ● Peripheral-to-memory, memory-to-peripheral, and peripheral-to-peripheral transfers 其他规格MCU的DMA不尽相同比如STM32F407仅DMA2具有存储器到存储器的传输方式而DMA1没有这种方式。 (2)DMA流 DMA流就是能进行DMA数据传输的链路是一个硬件结构所以每个DMA有独立的中断地址具有多个中断事件源如传输半完成中断事件、传输完成中事件等。每个DMA控制器有8个DMA流每个DMA流有独立的4级32位FIFO缓冲区。DMA流有很多参数这些参数的配置决定了DMA传输属性。 (3)DMA请求 DMA请求就是外设或存储器发起的DMA传输需求又称为DMA通道。每个DMA流最多有8个可选的DMA请求一个DMA请求一般有两个可选的DMA流。 (4)仲裁器 DMA控制器中有一个仲裁器仲裁器为两个AHB主端口(存储器和外设端)提供基于优先级别的DMA请求管理。每个DMA流有一个可设置的软件优先级别如果个DMA流的软件优先级别相同则流编号更小的优先级别更高。流编号就是DMA流的硬件优先级别。 (5)DMA传输属性 一个DMA流配置一个DMA请求后就构成一个单方向的DMA数据传输链路DMA传输属性就由DMA流的参数配置决定。DMA传输有如下一些属性 DMA流和通道。一个DMA流需要选择一个通道后才能组成一个DMA传输链路通道就是外设或存储器的DMA请求。DMA流的优先级别。需要为DMA流设置软件优先级别。源地址和目标地址。DMA传输是单方向的需要设置DMA传输的源地址和目标地址。源和目标的数据宽度即单个数据点的大小有字节、半字和字。传输数据量的大小。一次DMA传输的数据缓冲区大小。源地址和目标地址指针是否自增加。DMA工作模式即正常(Normal)模式或循环(Circular)模式。DMA传输模式。根据源和目标的特性所确定的数据传输方向DMA传输模式包括外设到存储器、存储器到外设以及存储器到存储器。是否使用FIFO以及使用FIFO时的阈值(Threshold)。是否使用突发传输以及源和目标突发传输数据量大小。是否使用双缓冲区模式。流量控制。 一个DMA传输链路的主要硬件是DMA流DMA传输属性的设置就是DMA流的参数配置。 2、源地址和目标地址 在32位的STM32 MCU中所有寄存器、外设和存储器是在4GB范围内统一编址的地址范围为0x00000000至0xFFFFFFFF。每个外设都有自己的地址外设的地址就是外设的寄存器基址。DMA传输由源地址和目标地址决定也就是整个4GB范围内可寻址的外设和存储器。 3、DMA传输模式 根据设置的DMA源和目标地址以及DMA请求的特性STM32G474的DMA数据传输有如下4种传输模式其它规格的MCU不尽相同比如STM32F407仅有3钟传输模式也就是数据传输方向。 外设到存储器(Peripheral To Memory)例如ADC采集的数据存入内存中的缓冲区。存储器到外设(Memory To Peripheral)例如通过UART接口发出内存中的数据。存储器到存储器(Memory To Memory)例如将外部SRAM中的数据复制到内存中。只有DMA2控制器有这种传输模式。外设到外设(Peripheral To Peripheral)STM32G474支持STM32F407不支持。 4、传输数据量的大小 默认情况下使用DMA作为流量控制器需要设置传输数据量的大小也就是从源到目标传输的数据总量。实际使用时传输数据量的大小就是一个DMA传输数据缓冲区的大小。 5、数据宽度 数据宽度(Data Width)是源和目标传输的基本数据单元的大小有字节(Byte)、半字(HanWord)和字(Word)3种大小。 源和目标的数据宽度是需要单独设置的。一般情况下源和目标的数据宽度是一样的。例如USART2使用DMA方式发送数据传输方向是存储器到外设因为USART2发送数据的基本单元是字节所以存储器和外设的数据宽度都应该设置为字节。 6、地址指针递增 可以设置在每次传输后将外设或存储器的地址指针递增或保持不变。 通过单个寄存器访问外设源或目标数据时应该禁止递增但是在某些情况下使地址递增可以提高传输效率。例如将ADC转换的数据以DMA方式存入内存时可以使存储器的地址递增这样每次传输的数据自动存入新的地址。外设和存储器的地址递增量的大小就是其各自的数据宽度。  7、DMA工作模式 DMA配置中要设置传输数据量大小也就是DMA发送或接收的数据缓冲区的大小。根据是否自动重复传输缓冲区的数据DMA工作模式分为正常模式和循环模式两种。 正常(Normal)模式是指传输完一个缓冲区的数据后DMA传输就停止了若需要再传输一次缓冲区的数据就需要再启动一次DMA传输。例如在正常模式下执行函数HAL_UART_Receive_DMA()接收固定长度的数据接收完成后就不再继续接收了这与中断方式接收函数HAL_UART_Receive_IT()类似。循环(Circular)模式是指启动一个缓冲区的数据传输后会循环执行这个DMA数据传输任务。例如在循环模式下只需执行一次HAL_UART_Receive_DMA(),就可以连续重复地进行串口数据的DMA接收接收满一个缓冲区的数据后产生DMA传输完成事件中断。这可以很好地解决串口输入连续监测的问题使程序结构简化。 8、DMA流的优先级别 每个DMA流都有一个可设置的软件优先级别(Priority level)优先级别有4种Very high(非常高)、High(高)、Medium(中等)和Low(低)。如果两个DMA流的软件优先级别相同则流编号更小的优先级别更高。流编号就是DMA流的硬件优先级。 DMA控制器中的仲裁器基于DMA流的优先级别进行DMA请求管理。  要区分DMA流中断优先级和DMA流优先级别这两个概念。DMA流中断优先级是NVIC管理的中断系统里的优先级而DMA流优先级别是DMA控制器里管理DMA请求用到的优先级。 9、FIFO或直接模式 每个DMA流有4级32位FIFO缓冲区DMA传输具有FIFO模式或直接模式。 不使用FIFO时就是直接模式直接模式就是发出DMA请求时立即启动数据传输。如果是存储器到外设的DMA传输DMA会预先取数据放在FIFO里发出DMA请求时立即将数据发送出去。 使用FIFO缓冲区时就是FIFO模式。可通过软件将阈值设置为FIFO的1/4、1/2、3/4或1倍大小。FIFO中存储的数据量达到阈值时FIFO中的数据就传输到目标中。 当DMA传输的源和目标的数据宽度不同时FIFO非常有用。例如源输出的数据是字节数据流而目标要求32位的字数据这时可以设置FIFO阈值为1倍这样就可以自动将4字节数据组合成32位字数据。 10、单次传输或突发传输 单次(Single)传输就是正常的传输方式在直接模式下(就是不使用FIFO时),只能是单次传输。 要使用突发(Burst)传输必须使用FIFO模式可以设置为4个、8个或16个节拍的增量突发传输。这里的节拍数并不是字节数。每个节拍输出的数据大小还与地址递增量大小有关每个节拍输出字节、半字或字。 为确保数据一致性形成突发的每一组传输都不可分割。在突发传输序列期间AHB传输会锁定并且AHB总线矩阵的仲裁器不解除对DMA主总线的授权。 11、双缓冲区模式 可以为DMA传输启用双缓冲区模式并自动激活循环模式。双缓冲区模式就是设置两个存储器指针在每次一个缓冲区传输完成后交换存储器指针DMA流的工作方式与常规单缓冲区一样。 在双缓冲区模式下每次传输完一个缓冲区时DMA控制器都从一个存储器目标切换到另一个存储器目标。这种模式在ADC数据采集时非常有用例如为ADC的DMA传输设置两个缓冲区即Buffer1和Buffer2。DMA交替使用这两个缓冲区存储数据当DMA使用Buffer1时程序就可以对已保存在Buffer2中的数据进行处理DMA完成一个缓冲区的传输切换使用Buffer2时程序又可以对Buffer1中的数据进行处理如此交替往复。 二、DMA的HAL驱动 1、DMA的HAL函数 DMA的HAL驱动程序头文件是stm32g4xx_hal_dma.h和stm32g4xx_hal_dma_ex.h。(STM32 F407单片机是stm32f4xx_hal_dma.h和stm32f4xx_hal_dma_ex.h)主要驱动函数如表 分组 函数名 功能描述 初始化 HAL_DMA_Init() DMA传输初始化配置 轮询方式 HAL_DMA_Start() 启动DMA传输不开启DMA中断 HAL_DMA_PollForTransfer() 轮询方式等待DMA传输结束可设置一个超时等待时间 HAL_DMA_Abort() 中止以轮询方式启动的 DMA传输 中断方式 HAL_DMA_Start_IT() 启动DMA传输开启DMA中断 HAL_DMA_Abort_IT() 中止以中断方式启动的 DMA传输 HAL_DMA_GetState() 获取DMA当前状态 HAL_DMA_IRQHandler() DMA中断ISR里调用的通用处理函数 双缓冲区模式 HAL_DMAEx_MultiBufferStar 启动双缓冲区DMA不开启DMA中断 HA_DMAEx_MultiBufferStart_IT() 启动双缓冲区DMA传输开启DMA中断 HAL_DMAEx_ChangeMemory() 传输过程中改变缓冲区地址 DMA是MCU上的一种比较特殊的硬件它需要与其他外设结合起来使用不能单独使用。一个外设要使用DMA传输数据必须先用函数HAL_DMA_Init()进行DMA初始化配置设置DMA流和通道、传输方向、工作模式(循环或正常)、源和目标数据宽度、DMA流优先级别等参数然后才可以使用外设的DMA传输函数进行DMA方式的数据传输。 DMA传输有轮询方式和中断方式。如果以轮询方式启动DMA数据传输则需要调用函数HAL_DMA_PollForTransfer()查询并等待DMA传输结束。如果以中断方式启动DMA数据传输则传输过程中DMA流会产生传输完成事件中断。每个DMA流都有独立的中断地址使用中断方式的DMA数据传输更方便所以在实际使用DMA时一般是以中断方式启动DMA传输。 DMA传输还有双缓冲区模式可用于一些高速实时处理的场合。例如ADC的DMA传输方向是从外设到存储器的存储器一端可以设置两个缓冲区在高速ADC采集时可以交替使用两个数据缓冲区一个用于接收ADC的数据另一个用于实时处理。 2、DMA传输初始化 函数HAL_DMA_Init()用于DMA传输初始化配置其原型定义如下 HAL_StatusTypeDef  HAL_DMA_Init(DMA_HandleTypeDef *hdma); 其中hdma是DMA_HandleTypeDef结构体类型指针。 结构体DMA_HandleTypeDef的成员指针变量Instance要指向一个DMA流的寄存器基址。其成员变量Init是结构体类型DMA_InitTypeDef它存储了DMA传输的各种属性参数。结构体DMA_HandleTypeDef还定义了多个用于DMA事件中断处理的回调函数指针。 结构体DMA_InitTypeDef的很多成员变量的取值是宏定义常量具体的取值和意义通过CubeMX的设置和自动生成的代码来解释。 在CubeMX中为外设进行DMA配置后在自动生成的代码里会有一个DMA_HandleTypeDef结构体类型变量。例如为USART2的DMA请求USART2_TX配置DMA后在生成的文件usart.c中有如下的变量定义称之为DMA流对象变量 DMA_HandleTypeDef hdma_usart2_rx; //DMA流对象变量 在USART2的外设初始化函数里为变量hdma_usart2_rx赋值hdma_usart2_rx.Instance指向一个具体的DMA流的寄存器基址hdma_usart2_ rx.Init的各成员变量设置DMA传输的各个属性参数然后执行HAL_DMA_Init(hdma_usart2_rx)进行DMA传输初始化配置。变量hdma_usart2_rx的基地址指针Instance指向一个DMA流的寄存器基址它还包含DMA传输的各种属性参数以及用于DMA事件中断处理的回调函数指针。所以将用结构体DMA_HandleTypeDef定义的变量称为DMA流对象变量。 3、启动DMA数据传输 完成DMA传输初始化配置后就可以启动DMA数据传输了。DMA数据传输有轮询方式和中断方式。每个DMA流都有独立的中断地址有传输完成中断事件使用中断方式的DMA数据传输更方便。函数HAL_DMA_Start_IT()以中断方式启动DMA数据传输其原型定义如下 HAL_StatusTypeDef HAL_DMA_Start_IT(DMA_HandleTypeDef *hdma,uint32_t SrcAddress,uint32_t DstAddress,uint32_t DataLength) 其中hdma是DMA流对象指针SrcAddress是源地址DstAddress是目标地址DataLength是需要传输的数据长度。 在使用具体外设进行DMA数据传输时一般无须直接调用函数HAL_DMA_Start_IT()启动DMA数据传输而是由外设的DMA传输函数内部调用函数HAL_DMA_Start IT()启动DMA数据传输。例如串口传输数据除了有阻塞方式和中断方式外还有DMA方式。串口以DMA方式发送数据和接收数据的两个函数的原型定义如下 HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart,uint8_t *pData,uint16_t Size) HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart,uint8_t*pData,uint16_t Size) 其中huart是串口对象指针pData是数据缓冲区指针缓冲区是uint8_t类型数组因为串口传输数据的基本单位是字节Size是缓冲区长度单位是字节。USART2使用DMA方式发送一个字符串的示意代码如下 uint8_t hello1[]Hello,DMA transmit\n; HAL_UART_Transmit_DMA(huart1,hello1,sizeof(hello1)); 函数HAL_UART_Transmit_DMA()内部会调用HAL_DMA_Start_IT()而且会根据USART2关联的DMA流对象的参数自动设置函数HAL_DMA_Start_IT()的输入参数如源地址、目标地址等。 4、DMA的中断 DMA的中断实际就是DMA流的中断。每个DMA流有独立的中断号有对应的ISR。DMA中断有多个中断事件源DMA中断事件类型的宏定义(也就是中断事件使能控制位的宏定义)如下 #define DMA_IT_TC ((uint32_t)DMA_SxCR_TCIE) //DMA传输完成中断事件 #define DMA_IT_HT ((uint32_t)DMA_SxCR_HTIE) //DMA传输半完成中断事件 #define DMA_IT_TE ((uint32_t)DMA_SxCR_TEIE) //DMA传输错误中断事件 #define DMA_IT_DME ((uint32_t)DMA_SxCR_DMEIE) //DMA直接模式错误中断事件 #define DMA_IT_FE 0x00000080U //DMA FIFO上溢/下溢中断事件 对一般的外设来说一个事件中断可能对应一个回调函数这个函数的名称是HAL库固定好了的例如UART的发送完成事件中断对应的回调函数名称是HAL_UART_TxCpltCallback()。但是在DMA的HAL驱动程序头文件stm32g4xx_hal_dma.h中并没有定义这样的回调函数因为DMA流是要关联不同外设的所以它的事件中断回调函数没有固定的函数名而是采用函数指针的方式指向关联外设的事件中断回调函数。DMA流对象的结构体DMA_HandleTypeDef的定义代码中有这些函数指针。 HAL_DMA_IRQHandler()是DMA流中断通用处理函数在DMA流中断的ISR里被调用。这个函数的原型定义如下其中的参数hdma是DMA流对象指针 void HAL_DMA_IRQHandler(DMA_HandleTypeDef *hdma) 通过分析函数HAL_DMA_IRQHandler()的源代码我们整理出DMA流中断事件与DMA流对象也就是结构体DMA_HandleTypeDef的回调函数指针之间的关系。 DMA流中断事件类型宏 DMA流中断事件 DMA_HandleTypeDef结构体中的函数指针 DMA_IT_TC 传输完成中断 XferCpltCallback DMA_IT_HT 传输半完成中断 XferHalfCpltCallback DMA_IT_TE 传输错误中断 XferErrorCallback DMA_IT_FE FIFO错误中断 无 DMA_IT_DME 直接模式错误中断 无 在DMA传输初始化配置函数HAL_DMA_Init()中程序不会为DMA流对象的事件中断回调函数指针赋值一般在外设以DMA方式启动传输时为这些回调函数指针赋值。例如对于UART执行函数HAL_UART Transmit_DMA()启动DMA方式发送数据时就会将串口关联的DMA流对象的函数指针XferCpltCallback指向UART的发送完成事件中断回调函数HAL_UART_TxCpltCallback()。 UART以DMA方式发送和接收数据时常用的DMA流中断事件与回调函数之间的关系如表所示。注意这里发生的中断是DMA流的中断而不是UART的中断DMA流只是使用了UART的回调函数。特别地DMA流有传输半完成中断事件(DMA_IT_HT)而UART是没有这种中断事件的UART的HAL驱动程序中定义的两个回调函数就是为了DMA流的传输半完成事件中断调用的。 UART的DMA传输函数 DMA流中断事件 DMA流对象的函数指针 DMA流事件中断关联的具体回调函数 HAL_UART_Transmit_DMA() DMA_IT_TC XferCpltCallback HAL_UART_TxCpltCallback() DMA_IT_HT XferHalfCpltCallback HAL_UART_TxHalfCpltCallback() HAL_UART_Receive_DMA() DMA_IT_TC XferCpltCallback HAL_UART_RxCpltCallback() DMA_IT_HT XferHalfCpltCallback HAL_UART_RxHalfCpltCallback() UART使用DMA方式传输数据时UART的全局中断需要开启但是UART的接收完成和发送完成中断事件源可以关闭。 三、工程配置 本文实例结合代码详细分析DMA的工作原理特别是DMA流的中断事件与外设的回调函数之间的关系。 本文实例的工程参考作者的文章细说STM32单片机USART中断收发RTC实时时间并改善其鲁棒性的方法_stm32串口中断时间-CSDN博客  https://wenchm.blog.csdn.net/article/details/143461698 1、设计目的和通讯协议 同参考文章。 2、工程设置 1时钟 外部高速时钟24MHzHSEAPB等都是170MHz外部低速时钟32.768KHzLSE32.768KHz to RTC 2DEBUG Serial Wire 3 RTC 首先启用LSE和RTC在时钟树上设置LSE作为RTC的时钟源。勾选Activate Clock Source和Activate Calendar选择Internal WakeupCalendar Time可以根据实际需要填写比如Data Format为Binary data formatHours13Minutes23Seconds15Calendar Date可以根据实际填写比如Week Day MondayMonth NovemberDate 11Year 24Wake Up Wake Up Clock(唤醒时钟源)为1Hz信号Wake Up Counter(唤醒计数器)值为0也就是每秒唤醒一次。其它参数默认 4USART2 Mode工作模式设置为Asynchronous(异步)也是串口最常用的模式Hardware Flow Control (RS232)硬件流控制设置为Disable。 参数设置部分包括串口通信的4个基本参数和STM32的2个扩展参数。 4个基本参数如下 Baud Rate设置为115200 bit/s。Word Length字长(包括奇偶校验位)设置为8位。Parity设置为None。如果设置有奇偶校验字长应该设置为9位。Stop Bits设置为1位。 STM32 MCU扩展的2个参数如下 Data Direction数据方向设置为Receive and Transmit(接收和发送)。还可以设置为只接收或只发送。Over Sampling过采样设置为16 Samples可选16 Samples或8 Samples。选择不同的过采样数值会影响波特率的可设置范围而CubeMX会自动更新波特率的可设置范围。其它参数默认 DMA Setting 5NVIC 6Project Manager Code Generater 同参考文章。 四、软件代码 1、main.c /* USER CODE BEGIN 2 */// The global interrupt of USART must be turned on, but the interrupt event can be turned off//__HAL_UART_DISABLE_IT(huart2, UART_IT_TC); //关闭USART2的发送完成IT//__HAL_UART_DISABLE_IT(huart2, UART_IT_RXNE); //关闭USART2的接收完成ITuint8_t hello1[]Hello,DMA transmit\n;HAL_UART_Transmit_DMA(huart2,hello1,sizeof(hello1)); //DMA方式transmitHAL_UART_Receive_DMA(huart2, rxBuffer,RX_CMD_LEN); //DMA方式循环接收 /* USER CODE END 2 */ /* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 *///这句很重要目的总是连续显示RTC时间//没有这句仅仅在程序下载后第?次运行连续显示RTC时间发送了指令后//只显示发送的指令字符串不再显示RTC时间这显然不符合设计目的??if(isUploadTime 1){HAL_RTCEx_WakeUpTimerEventCallback(hrtc);}} /* USER CODE END 3 */ while循环里的代码经过测试作者是必须的如果没有第一次下载的时候是能够实现RTC时间连续显示的但是MCU重启后是不能连续下载的。具体到个人的应用到底要不要这段程序要根据个人的实测结果来决定。 2、usart.h /* USER CODE BEGIN Includes */ #define RX_CMD_LEN 5 // string length extern uint8_t rxBuffer[]; // Serial port receiving data bufferextern uint8_t isUploadTime; // upload RTCtime switch /* USER CODE END Includes */ /* USER CODE BEGIN Prototypes */ void updateRTCTime(); /* USER CODE END Prototypes */ 3、usart.c * USER CODE BEGIN 0 */ #include rtc.h #include dma.h #include string.h #include ctype.huint8_t proBuffer[10]; //用于处理数据, #H12; #M23; #S43; uint8_t rxBuffer[10]; //接收缓存数据, #H12; #M23; #S43; uint8_t isUploadTime1; //是否上传时间数据unsigned char hello1[]Invalid command\n; unsigned char hello2[]Invalid data\n;/* USER CODE END 0 */ /* USER CODE BEGIN 1 */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {if (huart-Instance USART2){for(uint16_t i0;iRX_CMD_LEN;i)proBuffer[i] rxBuffer[i];// Upload the received command string and must be delayed,// otherwise updateRTCTime() will error.HAL_UART_Transmit_DMA(huart,rxBuffer,RX_CMD_LEN1);HAL_Delay(10);// Identify the start_bit is # and the end_bit is ;or not.// Determine whether the number of characters received is equal to 5.if (rxBuffer[0] ! # || rxBuffer[RX_CMD_LEN -1] ! ;){HAL_UART_Init(huart2); //重启串口HAL_UART_Transmit(huart2,hello1,sizeof(hello1),200);memset(rxBuffer, \0, sizeof(rxBuffer));memset(proBuffer, \0, sizeof(proBuffer));return;//已经发生错误自然退出这个回调函数}// Identify the data_bit is digits or notif (isalpha(proBuffer[2]) || isalpha(proBuffer[3])){HAL_UART_Init(huart2);HAL_UART_Transmit(huart2,hello2,sizeof(hello2),200);memset(rxBuffer, \0, sizeof(rxBuffer));memset(proBuffer, \0, sizeof(proBuffer));return;}updateRTCTime(); //指令解析处理} }//根据串口接收的指令字符串进行update void updateRTCTime() {uint8_t timeSectionproBuffer[1]; //类型字符, #H12;uint8_t tmp10proBuffer[2]-0x30; //十位uint8_t tmp1 proBuffer[3]-0x30; //个位uint8_t val10*tmp10tmp1;//update RTCtimeRTC_TimeTypeDef sTime;RTC_DateTypeDef sDate;if (HAL_RTC_GetTime(hrtc, sTime, RTC_FORMAT_BIN) HAL_OK){// After calling HAL_RTC_GetTime(),// you must call HAL_RTC_GetDate() to continuously update Date and Time.HAL_RTC_GetDate(hrtc, sDate, RTC_FORMAT_BIN);switch (timeSection){case H: // Modify hours{if(val 24)sTime.Hours val;else{HAL_UART_Init(huart2);HAL_UART_Transmit(huart2,hello2,sizeof(hello2),200);memset(proBuffer, \0, sizeof(proBuffer));return;}}break;case M: // Modify minutes{if(val 60)sTime.Minutes val;else{HAL_UART_Init(huart2);HAL_UART_Transmit(huart2,hello2,sizeof(hello2),200);memset(proBuffer, \0, sizeof(proBuffer));return;}}break;case S: // Modify seconds{if(val 60)sTime.Seconds val;else{HAL_UART_Init(huart2);HAL_UART_Transmit(huart2,hello2,sizeof(hello2),200);memset(proBuffer, \0, sizeof(proBuffer));return;}}break;case U:{if( tmp1 0){isUploadTime 0;//pausereturn;}elseisUploadTime 1; //resume}break;default: // If it is not H, M, S, U then return{HAL_UART_Init(huart2);HAL_UART_Transmit(huart2,hello1,sizeof(hello1),200);memset(proBuffer, \0, sizeof(proBuffer));}return;}//Set the RTC time and will affect the next RTC wake-up interrupt.HAL_RTC_SetTime(hrtc, sTime, RTC_FORMAT_BIN);} } /* USER CODE END 1 *//* USER CODE BEGIN 1 */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {if (huart-Instance USART2){for(uint16_t i0;iRX_CMD_LEN;i)proBuffer[i] rxBuffer[i];// Upload the received command string and must be delayed,// otherwise updateRTCTime() will error.HAL_UART_Transmit_DMA(huart,rxBuffer,RX_CMD_LEN1);HAL_Delay(10);// Identify the start_bit is # and the end_bit is ;or not.// Determine whether the number of characters received is equal to 5.if (rxBuffer[0] ! # || rxBuffer[RX_CMD_LEN -1] ! ;){HAL_UART_Init(huart2); //重启串口HAL_UART_Transmit(huart2,hello1,sizeof(hello1),200);memset(rxBuffer, \0, sizeof(rxBuffer));memset(proBuffer, \0, sizeof(proBuffer));return;//已经发生错误自然退出这个回调函数}// Identify the data_bit is digits or notif (isalpha(proBuffer[2]) || isalpha(proBuffer[3])){HAL_UART_Init(huart2);HAL_UART_Transmit(huart2,hello2,sizeof(hello2),200);memset(rxBuffer, \0, sizeof(rxBuffer));memset(proBuffer, \0, sizeof(proBuffer));return;}updateRTCTime(); //指令解析处理} }//根据串口接收的指令字符串进行update void updateRTCTime() {uint8_t timeSectionproBuffer[1]; //类型字符, #H12;uint8_t tmp10proBuffer[2]-0x30; //十位uint8_t tmp1 proBuffer[3]-0x30; //个位uint8_t val10*tmp10tmp1;//update RTCtimeRTC_TimeTypeDef sTime;RTC_DateTypeDef sDate;if (HAL_RTC_GetTime(hrtc, sTime, RTC_FORMAT_BIN) HAL_OK){// After calling HAL_RTC_GetTime(),// you must call HAL_RTC_GetDate() to continuously update Date and Time.HAL_RTC_GetDate(hrtc, sDate, RTC_FORMAT_BIN);switch (timeSection){case H: // Modify hours{if(val 24)sTime.Hours val;else{HAL_UART_Init(huart2);HAL_UART_Transmit(huart2,hello2,sizeof(hello2),200);memset(proBuffer, \0, sizeof(proBuffer));return;}}break;case M: // Modify minutes{if(val 60)sTime.Minutes val;else{HAL_UART_Init(huart2);HAL_UART_Transmit(huart2,hello2,sizeof(hello2),200);memset(proBuffer, \0, sizeof(proBuffer));return;}}break;case S: // Modify seconds{if(val 60)sTime.Seconds val;else{HAL_UART_Init(huart2);HAL_UART_Transmit(huart2,hello2,sizeof(hello2),200);memset(proBuffer, \0, sizeof(proBuffer));return;}}break;case U:{if( tmp1 0){isUploadTime 0;//pausereturn;}elseisUploadTime 1; //resume}break;default: // If it is not H, M, S, U then return{HAL_UART_Init(huart2);HAL_UART_Transmit(huart2,hello1,sizeof(hello1),200);memset(proBuffer, \0, sizeof(proBuffer));}return;}//Set the RTC time and will affect the next RTC wake-up interrupt.HAL_RTC_SetTime(hrtc, sTime, RTC_FORMAT_BIN);} } /* USER CODE END 1 */ usart.c的程序里包含异常情况下的容错处理。 4、rtc.c /* USER CODE BEGIN 0 */ #include usart.h #include stdio.h //用到函数sprintf() #include string.h //用到函数strlen()uint8_t second 100; //大于60的intsTime.Seconds /* USER CODE END 0 */ /* USER CODE BEGIN 1 */ void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc) {RTC_TimeTypeDef sTime;RTC_DateTypeDef sDate;if (HAL_RTC_GetTime(hrtc, sTime, RTC_FORMAT_BIN) HAL_OK){HAL_RTC_GetDate(hrtc, sDate, RTC_FORMAT_BIN);//显示 时间 hh:mm:ssuint8_t timeStr[20]; //RTCtime stringsprintf((char *)timeStr,%2d:%2d:%2d\n,sTime.Hours,sTime.Minutes,sTime.Seconds);if ((isUploadTime 1) ((uint8_t)sTime.Seconds ! second)){second (uint8_t)sTime.Seconds;HAL_UART_Transmit_DMA(huart2,timeStr,strlen ((const char *)(timeStr))); // send updated data.HAL_Delay(10); //若要上位机正常显示换行必须要有这个延时}} } /* USER CODE END 1 */ 五、运行与调试 下载运行首先显示字符串“Hello,DMA transmit”然后连续显示时间间隔1s。下面根据不同的指令输入情况展示运行结果。 1、合规的指令  输入正确的时、分、秒、暂停、恢复、及再次输入正确的指令 2、proBuffer[0]不是#或proBuffer[4]不是 输入字符串长度5但首字符≠#或结束字符≠时能正常进行容错处理并消息提示可以继续输入正确的指令 3、指令长度小于5 输入字符串的长度5第一次输入没有显示第二次及以后的输入有显示并错误提示虽然还显示RTC时间但是并没有改变RTC时间。直至累计输入的字符是5的倍数以后才跳出错误循环此后输入正确的指令后执行并显示正确的结果。 比如输入#H8;直到输入第5次时才跳出错误循环此后输入#S34;正确修改秒并显示输入U00暂停U01恢复。 4、仅proBuffer[2]或proBuffer[3]不是数字 显示数据错误。 5、 ;位于proBuffer[2]或proBuffer[3]位置 显示指令错误。 6、proBuffer[2]和proBuffer[3]数字超范围 显示数据错误。 7、指令长度大于5 当输入的指令长度大于5时显示指令错误并不改变RTC时间直到累计输入的指令的长度恰好等于5时跳出纠错循环回到正确数据处理的状态此时如果输入正确的指令将会修改RTC时间并连续显示。 比如输入#H123;指令长度6直到连续输入5次后再输入正确的指令比如输入#S34;正确地修改秒并连续显示输入#U00暂停输入#U01恢复。 特别地当输入指令的长度恰好是5的倍数比如10那么每次输入都有出错提示并且每次输入之后都可以继续输入并执行正确的指令。 当输入的指令的长度不等于5时程序容错能力是比较弱的鲁棒性并不明显。这是因为串口接收设置数据长度5导致的rxBuffer[5]以后内容并不能被memset()清空残余的数据影响了紧邻的下一次Recieve。 当串口接收设置数据长度1时作者会在另一文章中给以分享容错程序会较好地解决此类情况程序的鲁棒性变得很好。
http://www.sczhlp.com/news/214222/

相关文章:

  • 苏州企业展厅设计公司优化工具 wordpress
  • 1营销型网站建设高端网站制作平台
  • 淮安做网站.卓越凯欣在线设计房屋装修app
  • 站长网网站模板wordpress 下 刷文章
  • 域名注册在那个网站好企业网站建立平台
  • 大兴网站开发公司一个页面多少钱
  • windows优化大师官方网站传奇手游源码网
  • dede 网站版权信息企业名录搜索软件靓号怎么搜
  • 云趣在线企业网站建设铜陵保障性住房和城乡建设网站
  • 湖南小企业网站建设怎么做开发一个网站需要多少人
  • 建设软件网站湖州网站设计浙北数据
  • 做婚礼设计在哪个网站下载素材凡科送审平台官网
  • 杭州教育培训网站建设wordpress刷关键
  • 网站地市频道建设康定网站建设
  • 法制建设网站wordpress移除仪表盘
  • 大连网站关键词排名推广引流渠道有哪些
  • 天津河东做网站哪家好河池市住房和城乡建设局网站
  • 可以做贺卡的网站门户类网站
  • 重庆平台网站建设找哪家西安做网站朋朋
  • 求个网站你明白的 知乎徐州建设公司网站
  • 佛山市制作网站深圳建设局官网站
  • 东莞网站建设 南城石佳房产中介公司网站源码
  • 商业网站建设平台室内装修设计软件推荐
  • 长沙点看网络科技有限公司seo3
  • 网站建设完成的时间做哪种网站赚钱
  • 响应式网站和传统网站网站描述标签
  • 上海川沙网站建设个人品牌网站设计
  • 读书笔记:深入理解java虚拟机
  • CSP-S 19
  • CSP-S 20