深圳网站seo关键词网站开发软件三剑客
- 作者: 五速梦信息网
- 时间: 2026年04月20日 09:19
当前位置: 首页 > news >正文
深圳网站seo关键词,网站开发软件三剑客,网站设计与开发的基本步骤包括哪些?,jsp网站首页那栏怎么做玩转 STM32 单片机#xff0c;肯定离不开串口。串口使用一个称为串行通信协议的协议来管理数据传输#xff0c;该协议在数据传输期间控制数据流#xff0c;包括数据位数、波特率、校验位和停止位等。由于串口简单易用#xff0c;在各种产品交互中都有广泛应用。 但在使用串…玩转 STM32 单片机肯定离不开串口。串口使用一个称为串行通信协议的协议来管理数据传输该协议在数据传输期间控制数据流包括数据位数、波特率、校验位和停止位等。由于串口简单易用在各种产品交互中都有广泛应用。 但在使用串口通讯的时候我们并不知道对方会发送多少个数据也不知道数据什么时候发送完简单来讲就是如何确保收到一帧完整的数据 串口发送的数据有长有短如果没有接收完整肯定会影响后续业务的处理。为了接收不定长数据常见的处理方法有
- 固定格式 比如双方约定一帧的数据以 AA BB 开头以 BB AA 结尾这样在从机接收数据的时候一旦收到 AA BB 字符就知道对方要发来一个数据包了然后就把后面发来的数据保存起来直到接收到 BB AA 为止。 这种方法简单高效但缺点就是需要每个字符都进行判断浪费 CPU 资源增加功耗。
- 接收中断超时判断 串口接收到一个数据时就会触发接收中断。但如何判断数据已经发送完了呢 通常来讲两帧数据之间会有个时间间隔。因此我们可以使用一个计时器如果在一个固定的时间点里没接收到新的字符则认为一帧数据接收完成了。
- 空闲中断 串口在空闲时也就是说串口在一段时间里没有接收到新数据则会触发空闲中断。细心的同学应该发现了空闲中断实际上跟上面的超时判断是一样样的只不过空闲中断是硬件自带但超时判断需要我们自己实现。 所以一旦接收到空闲中断可以认为接收到一帧完整的数据。 但是空闲中断并不是所有的 MCU 都具备一般高端一点的 MCU 才有低端一些的 MCU 并没有空闲中断。
- 源码下载及前置阅读 本文首发 良许嵌入式网 https://www.lxlinux.net/e/ 欢迎关注 本文所涉及的源码及安装包如下由于平台限制请点击以下链接阅读原文下载 https://www.lxlinux.net/e/stm32/stm32-usart-receive-data-using-idle-dma.html 如果你是个零基础的小白连 STM32 都没见过我也给你准备了一个保姆级教程手把手教你搭建好 STM32 开发环境并教你如何下载程序简直业界良心 https://www.lxlinux.net/e/stm32/stm32-quick-start-for-beginner.html 如果你连代码都不知道怎么烧录到 STM32 的可以参考下文提供了 5 种代码烧录方式 https://www.lxlinux.net/e/stm32/five-ways-to-flash-program-to-stm32.html 如果你想自己搭一个属于自己的工程模板可以参考下面这篇文章 https://www.lxlinux.net/e/stm32/create-stm32-hal-project-template.html 在本文中我们详细来介绍如何使用接收中断超时判断完成不定长数据的接收对于接收中断的接收请查看下文 https://www.lxlinux.net/e/stm32/stm32-usart-receive-data-using-rxne-time-out.html
- 什么是空闲中断 前文已经提到当接收到一字节数据时会触发接收中断对应串口状态寄存器第 5 位被置 1 如果串口在空闲时则会触发空闲中断第 4 位被置 1 如下图所示 在中断服务函数里记得一定要清除 IDLE 位否则将一直触发空闲中断影响后续的业务处理。
- DMA
3.1 什么是DMA
令人头秃的描述 DMADirect Memory Access直接存储器访问提供在外设与内存、存储器和存储器、外设与外设之间的高速数据传输使用。它允许不同速度的硬件装置来沟通而不需要依赖于 CPU 在这个时间中CPU 对于内存的工作来说就无法使用。 简单描述
就是一个数据搬运工
3.2 DMA的意义
代替 CPU 搬运数据为 CPU 减负。 数据搬运的工作比较耗时间 数据搬运工作时效要求高有数据来就要搬走 没啥技术含量CPU 节约出来的时间可以处理更重要的事。 3.3 搬运什么数据
存储器、外设 这里的外设指的是 spi、usart、iic、adc 等基于APB1 、APB2 或 AHB 时钟的外设而这里的存储器包括自身的闪存flash或者内存SRAM以及外设的存储设备都可以作为访问地源或者目的。 三种搬运方式
存储器→存储器例如复制某特别大的数据 buf 存储器→外设 例如将某数据 buf 写入串口 TDR 寄存器外设→存储器 例如将串口 RDR 寄存器写入某数据 buf
存储器→存储器 存储器→外设 外设→存储器 3.4 DMA 控制器 STM32F103 有 2 个 DMA 控制器DMA1 有 7 个通道DMA2 有 5 个通道。对于 STM32F103C8T6 这颗芯片只有 DMA1 。 一个通道每次只能搬运一个外设的数据 如果同时有多个外设的 DMA 请求则按照优先级进行响应。 DMA1 有 7 个通道
DMA2 有 5 个通道
3.5 DMA及通道的优先级 优先级管理采用软件硬件 软件 每个通道的优先级可以在 DMA_CCRx 寄存器中设置有4个等级 最高级高级中级低级 硬件 如果 2 个请求它们的软件优先级相同则较低编号的通道比较高编号的通道有较高的优先权。 比如如果软件优先级相同通道 2 优先于通道 4
3.6 DMA传输方式 DMA_Mode_Normal正常模式 一次 DMA 数据传输完后停止 DMA 传送 也就是只传输一次 DMA_Mode_Circular循环传输模式 当传输结束时硬件自动会将传输数据量寄存器进行重装进行下一轮的数据传输。 也就是多次传输模式
3.7 指针递增模式 外设和存储器指针在每次传输后可以自动向后递增或保持常量。当设置为增量模式时下一个要传输的地址将是前一个地址加上增量值。 4. 硬件准备 STM32 核心板 本文使用 STM32F103C8T6 核心板非常便宜某宝上 10 元左右关键词STM32 核心板一杯奶茶的钱不到。 核心板最大的优点是便宜简单缺点就是需要根据需求自己搭一些电路对你的动手能力要求比较高。 上面所推荐的这块核心板主控芯片是 STM32103C8T6 64K flash20K RAM4 个定时器3 个串口网络上资料好几吨非常适合初学者入门强烈推荐。 USB 转 TTL 这种设备主要作用是用来调试或下载程序。价格也很便宜普遍 58 元。 ST-Link ST-Link 是一种用于 STM32 微控制器的调试和编程工具它可以通过 SWD 或 JTAG 接口与开发板进行通信。一般也很便宜七八元左右。 5. 编程实战 在本实验中我们将串口 1 作为 log 输出端口串口 2 作为本次实验的接收端口。 因此我们需要提前创建 uart2 模块包含 uart2.c 及 uart2.h 两个文件并加载进工程模板。 5.1 串口初始化 串口的初始化大家应该不陌生主要步骤为 定义串口句柄 uart2_handle 并调用 HAL_UART_Init 进行初始化初始化串口底层函数调用 HAL_UART_MspInit 函数。 第一步在 uart2.c 文件里进行 UART_HandleTypeDef uart2_handle;void uart2_init(uint32_t baudrate) {uart2_handle.Instance UART2_INTERFACE; /* UART2 /uart2_handle.Init.BaudRate baudrate; / 波特率 /uart2_handle.Init.WordLength UART_WORDLENGTH_8B; / 数据位 /uart2_handle.Init.StopBits UART_STOPBITS_1; / 停止位 /uart2_handle.Init.Parity UART_PARITY_NONE; / 校验位 /uart2_handle.Init.Mode UART_MODE_TX_RX; / 收发模式 /uart2_handle.Init.HwFlowCtl UART_HWCONTROL_NONE; / 无硬件流控 /uart2_handle.Init.OverSampling UART_OVERSAMPLING_16; / 过采样 /HAL_UART_Init(uart2_handle); / 使能UART2 */ }第二步在 usart.c 文件里进行其实也可以在 uart2.c 文件里做但我懒 在最下面两行代码我们使用 HAL_UART_ENABLE_IT() 使能接收中断及空闲中断。 void HAL_UART_MspInit(UART_HandleTypeDef huart) {GPIO_InitTypeDef gpio_init_struct;if (huart-Instance USART_UX) / 如果是串口1进行串口1 MSP初始化 /{….// 节略串口1相关代码….}else if (huart-Instance UART2_INTERFACE) / 如果是UART2 /{UART2_TX_GPIO_CLK_ENABLE(); / 使能UART2 TX引脚时钟 /UART2_RX_GPIO_CLK_ENABLE(); / 使能UART2 RX引脚时钟 /UART2_CLK_ENABLE(); / 使能UART2时钟 /gpio_init_struct.Pin UART2_TX_GPIO_PIN; / UART2 TX引脚 /gpio_init_struct.Mode GPIO_MODE_AF_PP; / 复用推挽输出 /gpio_init_struct.Pull GPIO_NOPULL; / 无上下拉 /gpio_init_struct.Speed GPIO_SPEED_FREQ_HIGH; / 高速 /HAL_GPIO_Init(UART2_TX_GPIO_PORT, gpio_init_struct); / 初始化UART2 TX引脚 /gpio_init_struct.Pin UART2_RX_GPIO_PIN; / UART2 RX引脚 /gpio_init_struct.Mode GPIO_MODE_INPUT; / 输入 /gpio_init_struct.Pull GPIO_NOPULL; / 无上下拉 /gpio_init_struct.Speed GPIO_SPEED_FREQ_HIGH; / 高速 /HAL_GPIO_Init(UART2_RX_GPIO_PORT, gpio_init_struct); / 初始化UART2 RX引脚 /HAL_NVIC_SetPriority(UART2_IRQn, 0, 0); / 抢占优先级0子优先级0 /HAL_NVIC_EnableIRQ(UART2_IRQn); / 使能UART2中断通道 */HAL_UART_ENABLE_IT(huart, UART_IT_RXNE); /* 使能UART2接收中断 /__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE); / 使能UART2总线空闲中断 /} }5.2 串口中断服务函数 前文已经提到串口触发到一次接收中断则代表接收到一个字符我们就可以把这个字符放到接收缓冲区里。这个过程与上一篇文章一样样可以参考下文 【STM32串口接收不定长数据接收中断超时判断】 具体代码实现如下 void UART2_IRQHandler(void) {uint8_t receive_data 0; if(HAL_UART_GET_FLAG(uart2_handle, UART_FLAG_RXNE) ! RESET){ //获取接收RXNE标志位是否被置位if(uart2_rx_len sizeof(uart2_rx_buf)) //如果接收的字符数大于接收缓冲区大小uart2_rx_len 0; //则将接收计数器清零HAL_UART_Receive(uart2_handle, receive_data, 1, 1000); //接收一个字符uart2_rx_buf[uart2_rx_len] receive_data; //将接收到的字符保存在接收缓冲区}…// 省略空闲中断代码… }串口触发一次空闲中断则代表接收到一帧数据也就是收到了一个完整的数据包了我们就可以将收到的数据包进行处理比如打印出来代码如下 void UART2_IRQHandler(void) {…// 省略接收中断代码…if (HAL_UART_GET_FLAG(uart2_handle, UART_FLAG_IDLE) ! RESET) //获取接收空闲中断标志位是否被置位{printf(recv: %s\r\n, uart2_rx_buf); //将接收到的数据打印出来uart2_rx_clear();__HAL_UART_CLEAR_IDLEFLAG(uart2_handle); //清除UART总线空闲中断} }在上面的代码里一定要记得调用 __HAL_UART_CLEAR_IDLEFLAG() 函数清除 UART 总线空闲中断否则空闲中断一直处于触发状态影响下一次接收。 判断是否收到接收/空闲中断需要用到的是 HAL_UART_GET_FLAG() 函数接收中断判断的是 UART_FLAG_RXNE 标志位而空闲中断判断的是 UART_FLAG_IDLE 标志位。 串口中断服务函数完整代码如下就是将上面两部分代码合二为一 void uart2_rx_clear(void) {memset(uart2_rx_buf, 0, sizeof(uart2_rx_buf)); //清空接收缓冲区uart2_rx_len 0; //接收计数器清零 }void UART2_IRQHandler(void) {uint8_t receive_data 0; if(HAL_UART_GET_FLAG(uart2_handle, UART_FLAG_RXNE) ! RESET){ //获取接收RXNE标志位是否被置位if(uart2_rx_len sizeof(uart2_rx_buf)) //如果接收的字符数大于接收缓冲区大小uart2_rx_len 0; //则将接收计数器清零HAL_UART_Receive(uart2_handle, receive_data, 1, 1000); //接收一个字符uart2_rx_buf[uart2_rx_len] receive_data; //将接收到的字符保存在接收缓冲区}if (HAL_UART_GET_FLAG(uart2_handle, UART_FLAG_IDLE) ! RESET) //获取接收空闲中断标志位是否被置位{printf(recv: %s\r\n, uart2_rx_buf); //将接收到的数据打印出来uart2_rx_clear();HAL_UART_CLEAR_IDLEFLAG(uart2_handle); //清除UART总线空闲中断} }对应的 uart2.h 文件完整代码如下 #include stdint.h #include usart.h/ 引脚定义 / #define UART2_TX_GPIO_PORT GPIOA #define UART2_TX_GPIO_PIN GPIO_PIN_2 #define UART2_TX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)#define UART2_RX_GPIO_PORT GPIOA #define UART2_RX_GPIO_PIN GPIO_PIN_3 #define UART2_RX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)#define UART2_INTERFACE USART2 #define UART2_IRQn USART2_IRQn #define UART2_IRQHandler USART2_IRQHandler #define UART2_CLK_ENABLE() do{ __HAL_RCC_USART2_CLK_ENABLE(); }while(0)/ 错误代码 / #define UART_EOK 0 / 没有错误 / #define UART_ERROR 1 / 通用错误 / #define UART_ETIMEOUT 2 / 超时错误 / #define UART_EINVAL 3 / 参数错误 // UART收发缓冲大小 / #define UART2_RX_BUF_SIZE 128 #define UART2_TX_BUF_SIZE 64void uart2_init(uint32_t baudrate);到这里实际上我们已经实现了使用空闲中断接收不定长数据的逻辑代码了烧进板子后效果如下 对于大多数应用场景下这种串口接收不定长数据的处理方式已经足够用了。 但如果你串口每次接收的数据量过于庞大那么就可以请出 DMA 这个数据搬运工了一旦接收到数据则立马搬走不占用 CPU 资源。 5.3 加入DMA 既然需要用到 DMA 外设则在 BSP 目录下创建 dma.c 及 dma.h 两个文件并加载进工程文件。 在 dma.c 文件里我们要做的事情就是初始化 DMA 外设实际上就是指定数据从哪里来、到哪里去以及数据长度等等。 由于我们使用的是串口2 RX 通道根据下图可知用到的 DMA 通道为 DMA1_Channel6 STM32F103C8T6只有 DMA1 。 在初始化的最后一定要记得调用 HAL_UART_Receive_DMA() 函数开启 DMA 接收否则 DMA 这个搬运工就算请过来了他还是依然不为你工作。 详细代码如下 void dma_init(void) {// UART2 RX DMA配置__HAL_RCC_DMA1_CLK_ENABLE(); / DMA1时钟使能 /dma_handle.Instance DMA1_Channel6; / USART2_RX使用的DMA通道为: DMA1_Channel6 /dma_handle.Init.Direction DMA_PERIPH_TO_MEMORY; / 外设到存储器模式 /dma_handle.Init.PeriphInc DMA_PINC_DISABLE; / 外设非增量模式 /dma_handle.Init.MemInc DMA_MINC_ENABLE; / 存储器增量模式 /dma_handle.Init.PeriphDataAlignment DMA_PDATAALIGN_BYTE; / 外设数据长度:8位 /dma_handle.Init.MemDataAlignment DMA_MDATAALIGN_BYTE; / 存储器数据长度:8位 /dma_handle.Init.Mode DMA_NORMAL; / 外设流控模式 /dma_handle.Init.Priority DMA_PRIORITY_LOW; / 低优先级 /HAL_DMA_Init(dma_handle);__HAL_LINKDMA(uart2_handle, hdmarx, dma_handle); / 将DMA与USART2联系起来(发送DMA) /HAL_UART_Receive_DMA(uart2_handle, uart2_rx_buf, UART2_RX_BUF_SIZE); / 开启DMA接收 */ }在串口中断服务函数里我们可以将接收中断相关代码全部去掉因为已经有了 DMA 这个搬运工了没必要让 CPU 一个个字符转移数据了。 我先把代码贴上来再详细讲解。 void UART2_IRQHandler(void) {if (HAL_UART_GET_FLAG(uart2_handle, UART_FLAG_IDLE) ! RESET){ //获取接收IDLE标志位是否被置位HAL_UART_CLEAR_IDLEFLAG(uart2_handle);HAL_UART_DMAStop(uart2_handle); //停止DMA传输防止干扰uart2_rx_len UART2_RX_BUF_SIZE - __HAL_DMA_GET_COUNTER(dma_handle); //获取接收到的数据长度printf(recv: %s, recv_len: %d\r\n, uart2_rx_buf, uart2_rx_len);uart2_rx_clear();HAL_UART_Receive_DMA(uart2_handle, uart2_rx_buf, UART2_RX_BUF_SIZE); //重新开启DMA传输} }上面的代码有几个要点需要解释一下 停止 DMA 传输 当我们收到空闲中断时实际上 DMA 已经帮我们把所有的数据搬运到了接收缓冲区了此时我们可以先把 DMA 传输关闭掉防止干扰到后续的操作。 获取接收到的数据长度 __HAL_DMA_GET_COUNTER() 函数表示 DMA 中待接收的数据长度。什么意思呢假设我需要 DMA 接收 100 个字符的数据量但现在实际上只接收到了 30 个字符那么待接收的数据长度为 70 也就是 __HAL_DMA_GET_COUNTER() 函数的返回值为 70 。 所以我们已经接收到的数据长度等于接收缓冲区的长度减去待接收的数据长度翻译成代码就是 uart2_rx_len UART2_RX_BUF_SIZE - __HAL_DMA_GET_COUNTER(dma_handle);重新开启 DMA 传输 一帧数据处理完成之后我们肯定要进行下一帧数据的接收所以需要调用 HAL_UART_Receive_DMA() 重新开启 DMA 传输否则数据只接收一帧之后就罢工了。 到此 DMA 就加入成功了烧进去板子后效果如下 6. 小结 STM32 串口通讯在项目中使用的频率非常高但由于不知道数据发送方会发送多少数据量所以串口接收不定长数据成了一个急需解决的问题。 本文使用串口的空闲中断DMA方法解决了此问题并给出了详细的教程希望对读者朋友有所帮助。
- 上一篇: 深圳网上招聘最好的网站网络制作网站
- 下一篇: 深圳网站创建公司长沙百度搜索排名优化
相关文章
-
深圳网上招聘最好的网站网络制作网站
深圳网上招聘最好的网站网络制作网站
- 技术栈
- 2026年04月20日
-
深圳外文网站制作零基础一个人做网站
深圳外文网站制作零基础一个人做网站
- 技术栈
- 2026年04月20日
-
深圳外贸英文网站设计公司哪家好建设部投诉网站
深圳外贸英文网站设计公司哪家好建设部投诉网站
- 技术栈
- 2026年04月20日
-
深圳网站创建公司长沙百度搜索排名优化
深圳网站创建公司长沙百度搜索排名优化
- 技术栈
- 2026年04月20日
-
深圳网站搭建找谁wordpress 折叠展开
深圳网站搭建找谁wordpress 折叠展开
- 技术栈
- 2026年04月20日
-
深圳网站订制开发上海著名的网站制作公司
深圳网站订制开发上海著名的网站制作公司
- 技术栈
- 2026年04月20日
