使用 STM32F407 串口实现 485 通信
准备工作
了解485通信基本概念与原理:RS485通信详解_485通讯de接什么口-CSDN博客
安装编译软件:keil uVision 5.6
软件资料:STM32CubeF4 固件包,正点原子RS485通信例程
参考视频:第26讲 基础篇-新建HAL版本MDK工程1_哔哩哔哩_bilibili
第27讲 基础篇-新建HAL版本MDK工程2_哔哩哔哩_bilibili
第156讲 入门篇- RS485_哔哩哔哩_bilibili
使用 STM32F407 串口实现 485 通信(半双工)
1.新建工程文件夹
1.1新建工程文件夹
首先新建一个工程根目录文件夹(最好不要出现中文路径),后续的工程文件都将在这个文件夹里建立,文件夹重命名为:485TestOne。
为了让工程的文件目录结构更加清晰易懂,在工程根目录文件夹下建立以下几个文件夹,每个文件夹名称及其作用如表 1.1 所示:
新建完成以后,最后得到的工程根目录文件夹如图 1.2 所示。
1.2拷贝工程相关文件
接下来,按根目录文件夹顺序介绍每个文件夹及其需要拷贝的文件。
1.2.1 Drivers 文件夹
该文件夹用于存放与硬件相关的驱动层文件,一般包括如表 1.2 所示的三个文件夹:
BSP 文件夹,用于存放正点原子提供的板级支持包驱动代码,如:LED、蜂鸣器、按键、RS485等。
CMSIS 文件夹,用于存放 CMSIS 底层代码(ARM 和 ST 提供),如:启动文件(.s 文件)、stm32f4xx.h 等各种头文件。该文件夹可以直接从 STM32CubeF4 固件包里面拷贝,但由于固件包里面的 CMISIS 兼容了太多芯片,会导致非常大(170 MB),因此可根据实际情况,对其进行精简。
SYSTEM 文件夹,用于存放正点原子提供的系统级核心驱动代码,如:sys.c、delay.c 和usart.c 等,方便大家快速搭建自己的工程。
BSP、CMSIS、SYSTEM文件夹可以之间从正点原子提供的RS485通信例程中拷贝。
STM32F4xx_HAL_Driver 文件夹,用于存放 ST 提供的 F4xx HAL 库驱动代码。该文件夹可以直接从 STM32CubeF4 固件包里面拷贝。直接拷贝“STM32CubeF4 固件包→Drivers”路径下的 “STM32F4xx_HAL_Driver”文件夹到工程的 Drivers 下,只保留 Inc 和 Src 文件夹即可。
执行完以上操作后,Drivers 文件夹最终结构如图 1.3 所示:
关于工程根目录下的 Drivers 文件操作到这里就完成了。
1.2.2 Middlewares 文件夹
该文件夹用于存放正点原子和其他第三方提供的中间层代码(组件/Lib 等),如:USMART、MALLOC、TEXT、FATFS、USB、LWIP、各种 OS、各种 GUI 等等。
1.2.3 Output 文件夹
该文件夹用于存放编译器编译工程输出的中间文件,比如:.hex、.bin、.o 文件等等。这里不需要操作,后面只需要在 MDK 里面设置该文件夹为编译过程中间文件的存放文件夹就行。
1.2.4 Projects 文件夹
该文件夹用于存放编译器(MDK、IAR 等)工程文件,工程主要用 MDK,为了方便区分,在该文件夹下新建:MDK-ARM 文件夹,用于存放 MDK 的工程文件,如图 1.4 所示:
1.2.5 User 文件夹
User 文件夹用于存放 HAL 库用户配置文件、main.c、中断处理文件,以及分散加载文件。
首先从官方固件包里面直接拷贝官方的模板工程下的 HAL 库用户配置文件和中断处理文件到 User 文件夹里。官方的模板工程路径:STM32Cube_FW_F4_V1.26.0\Projects\STM324xG_EVAL\Templates,打开Template_Project 文件夹,如图 1.5 所示。
在 Inc 和 Src 两个文件夹里面找到:stm32f4xx_it.c、 stm32f4xx_it.h、stm32f4xx_hal_conf.h 这三个文件,并且拷贝到 User 文件夹下。
注意:main.c文件也是放在User文件夹里面的,后面需要在MDK 里面新建.c 文件并保存。
2. 新建一个工程框架
2.1 首先,打开 MDK 软件。然后点击 Project→New uVision Project 如图 2.1 所示:
2.2 然后弹出工程命名和保存的操作窗口,将工程文件保存路径设置在上一节新建的工程文件夹内,具体路径为:485TestOne→Projects→MDK-ARM,
工程名字为:atk_f407,最后点击保存即可。具体操作窗口如图 2.2所示:
2.3 之后,弹出器件选择对话框,如图2.3所示。
因为探索者 STM32F407 开发板所使用的STM32 型号为 STM32F407ZGT6,所以选择:STMicroelectronics → STM32F4 Series →STM32F407 → STM32F407ZGTx(如果使用的是其他系列的芯片,选择相应的型号就可以了,特别注意:一定要安装对应的器件 pack 才会显示这些内容哦!!如果没得选择,请关闭 MDK,安装后重试。
2.4 点击 OK,MDK 会弹出 Manage Run-Time Environment 对话框,如图2.4所示:
这是 MDK5 新增的一个功能,在这个界面,可以添加自己需要的组件,从而方便构建开发环境,这里不做介绍。直接点击 Cancel,即可,
2.5 得到如图 2.5 所示界面:
2.6 此时,打开 MDK-ARM 文件夹,会看到 MDK 在该文件夹下自动创建了 3 个文件夹 (DebugConfig、Listings 和 Objects),如图所示:
这三个文件夹的作用:
DebugConfig:用于存放调试设置信息文件(.dbgconf),不可删除!
Listings:用于存放编译过程产生的链接列表等文件
Objects:用于存放编译过程产生的调试信息、.hex、预览、.lib 文件等
编译过程产生的链接列表、调试信息、预览、lib 等文件,统称为中间文件。为了统一管理,方便使用,可以把输出在 Listings 和 Objects 文件夹的内容,统一改为输出到 Output 文件夹(通过魔术棒设置),这里先把 MDK 自动生成的这两个文件夹(Listings 和 Objects)删除。
至此,完成了框架的构建,还有好几个步骤要做,比如添加文件、魔术棒设置、编写 main.c 等
3. 添加文件
本节将分 5 个步骤:1,设置工程名和分组名;2,添加启动文件;3,添加 SYSTEM 源码4,添加 User 源码;5,添加 STM32F4xx_HAL_Driver 源码。
3.1 设置工程名和分组名
3.1.1在 Project→Target 上右键,选择 Manage Project Items…(方法一)或在菜单栏点击品字形红绿白图标(方法二)进入工程管理界面,如图 3.1 所示:
3.1.2 在工程管理界面,可执行设置工程名字(Project Targets)、分组名字(Groups)以及添加每个分组的文件(Files)等操作。设置工程名字为:RS485,并设置以下分组:
Startup(存放启动文件)
User(存放 main.c 等用户代码)
Drivers/SYSTEM(存放系统级驱动代码)
Driver/STM32F4xx_HAL_Driver(存放 HAL 库代码)
Driver/BSP(存放板级支持包驱动代码,如:LED、按键等)
Middleware/USMART(存放中间层代码)
Readme(存放工程说明文件,可选)
如图 3.2 所示:
3.1.3 设置好之后,点击 OK,回到 MDK 主界面,可以看到设置的工程名和分组名如图所示:
注意:为了让工程结构清晰,尽量使 MDK 的工程分组和前面新建的工程文件夹对应起来,由于 MDK 分组不支持多级目录,因此将路径也带入分组命名里面,以便区 分。如:User 分组对应 User 文件夹里面的源码,Drivers/SYSTEM 分组,对应 Drivers/SYSTEM 文件夹里面的源码,Drivers/BSP 分组对应 Drivers/BSP 文件夹里面的源码等。
3.2 添加启动文件
启动文件(.s 文件)包含 STM32 的启动代码,其主要作用包括:1、堆栈(SP)的初始化;2、初始化程序计数器(PC);3、设置向量表异常事件的入口地址;4、调用 main 函数等,是每个工程必不可少的一个文件。
3.2.1 启动文件由 ST 官方提供,存放在 STM32CubeF4 软件包的:Drivers → CMSIS → Device →ST → STM32F4xx → Source → Templates → arm 文件夹下。开发板STM32F407ZGT6对应的启动文件为:startup_stm32f407xx.s。
有两种方法给 MDK 的分组添加文件:1,双击 Project 下的分组名添加。2,进入工程管理界面添加。这里我们使用方法 1 添加(路径:实验 0-3,新建工程实验-HAL 库版本\Drivers\CMSIS\Device\ST\STM32F4xx\Source\Templates\arm),如图3.4所示:
上图中,也可以点击 Add 按钮进行文件添加。添加完后,点击 Close,完成启动文件添加,得到工程分组如图所示:
3.3 添加 SYSTEM 源码
3.3.1 这里使用工程管理界面(方法 2)进行 SYSTEM 源码添加。点击: 按钮,进入工程管 理界面,选中 Drivers/SYSTEM 分组,然后点击:Add Files,进入文件添加对话框,在依次添加 delay.c、sys.c 和 usart.c 到该分组下,如图 3.6 所示:
注意:这些源码都是在第 1.2 小节的第二步拷贝过来的,如果之前没拷贝,是找不到这些源码的。
3.3.2 添加完成后,如图3.7所示:
3.4 添加 User 源码
和3.2步骤一样在工程管理界面(方法 2)进行 User 源码添加。点击: 按钮,进入工程管理界面,选中 User 分组,然后点击:Add Files,进入文件添加对话框,依次添加 stm32f4xx_it.c (480TestOne\User目录下)和system_stm32f4xx.c(480TestOne\Drivers\CMSIS\Device\ST\STM32F4xx\Source\Templates目录下) 到该分组下。
注意:这些源码都是在第 1.2 小节的第二步拷贝过来的,如果之前没拷贝,是找不到这些源码的。添加完成后,如图 3.8 所示:
3.5 添加 STM32F4xx_HAL_Driver 源码
和3.2步骤一样在工程管理界面(方法 2),选中 Drivers/STM32F4xx_HAL_Driver 分组,然后点击:Add Files,进入文件添加对话框,在480TestOne\Drivers\STM32F4xx_HAL_Driver\Src目录下找到文件,依次添加 stm32f4xx_hal.c 、 stm32f4xx_hal_cortex.c 、stm32f4xx_hal_dma.c 、stm32f4xx_hal_dma_ex.c、stm32f4xx_hal_gpio.c、 stm32f4xx_hal_pwr.c、stm32f4xx_hal_pwr_ex.c、stm32f4xx_hal_rcc.c、stm32f4xx_hal_rcc_ex.c、stm32f4xx_hal_sram.c、stm32f4xx_hal_tim.c、stm32f4xx_hal_tim_ex.c、stm32f4xx_hal_uart.c、stm32f4xx_hal_usart.c 、stm32f4xx_ll_fsmc.c到该分组下,如图3.9所示:
注意:如果看到分组中有些.c 文件有个小钥匙的符号,这是因为官方的固件包的文件设置了只读权限,取消480TestOne\Drivers\CMSIS与480TestOne\Drivers\STM32F4xx_HAL_Driver两个文件夹的只读权限就好了。
3.6 添加 BSP 源码
这里和3.2步骤一样在工程管理界面(方法 2)进行 User 源码添加。点击: 按钮,进入工程管理界面,选中 User 分组,然后点击:Add Files,进入文件添加对话框,依次添加 key.c(480TestOne\Drivers\BSP\KEY目录下)、 led.c(480TestOne\Drivers\BSP\LED目录下)、lcd.c(480TestOne\Drivers\BSP\LCD目录下) 、rs485.c (480TestOne\Drivers\BSP\RS485目录下)到该分组下。
注意:这些源码都是在第 1.2 小节的第二步拷贝过来的,如果之前没拷贝,是找不到这些源码的。添加完成后,如图3.10 所示:
3.7 添加 USMART 源码
这里和3.2步骤一样在工程管理界面(方法 2)进行 User 源码添加。点击: 按钮,进入工程管理界面,选中 User 分组,然后点击:Add Files,进入文件添加对话框,依次添加 usmart.c(480TestOne\Drivers\BSP\KEY目录下)、 led.c(480TestOne\Drivers\BSP\LED目录下)、lcd.c(480TestOne\Drivers\BSP\LCD目录下) 到该分组下。
注意:这些源码都是在第 1.2 小节的第二步拷贝过来的,如果之前没拷贝,是找不到这些源码的。添加完成后,如图 3.11 所示:
4. 魔术棒设置
为避免编写代码和编译报错,需要通过魔术棒对 MDK 工程进行相关设置。在 MDK主界面,点击: (魔术棒图标,即 Options for Target 按钮),进入工程设置对话框,将进行如下几个选项卡的设置。
4.1 设置 Target 选项卡
在图 4.1 中,设置芯片所使用的外部晶振频率为 8Mhz(uVision 5.36及以上版本是灰色,不用设置),选择 ARM Compiler 版本为:Use default compiler version 5(即 AC5 编译器)。
AC5 和 AC6 编译的差异:
4.2 设置 Output 选项卡
在魔术棒→Output 选项卡里面,进行如图 4.2 所示设置
注意:勾选:Browse Information,可用于输出浏览信息,就能使用 go to definition查看函数/变量的定义,对后续调试代码比较有帮助,如果不需要调试代码,则可以去掉勾选,以提高编译速度。
4.3 设置 Listing 选项卡
在魔术棒→Listing 选项卡里面,进行如图 4.3 所示设置:
经过 Output 和 Listing 这两步设置,原来存储在 Objects 和 Listings 文件夹的内容(中间文件)就都改为输出到 Output 文件夹了。
4.4 设置 C/C++选项卡
在魔术棒→C/C++选项卡里面,进行如图 4.4 所示设置:
在②处设置了全局宏定义:USE_HAL_DRIVER和STM32F407xx,他们之间用英文逗号隔开。STM32F407xx用于定义所用 STM32 型号,在 stm32f4xx.h 里面会用到该宏定义。
在③处设置了优化等级为-O0,可以得到最好的调试效果,当然为了提高优化效果提升性能并降低代码量,可以设置-O1~-O3,数字越大效果越明显,不过也越容易出问题。注意:当使用AC6 编译器的时候,这里推荐默认使用-O1 优化。
在④处勾选 C99 模式,即使用 C99 C 语言标准。
在⑤处,进行头文件包含路径设置,点击此按钮,进行如图 所示设置:
上图中设置了 6 个头文件包含路径,其中 3 个在 Drivers 文件夹下,一个在 User 文件夹下,一个在 Middlewares 文件夹下。为避免频繁设置头文件包含路径,正点原子最新源码的include 全部使用相对路径,即只需要在头文件包含路径里面指定一个文件夹,那么该文件夹下的其他文件夹里面的源码全部使用相对路径,则无需再设置头文件包含路径了,直接在 include 里面就指明了头文件所在。
关于相对路径,这里大家记住 3 点:
1,默认路径就是指 MDK 工程所在的路径,即.uvprojx 文件所在路径(文件夹)
2,“./”表示当前目录(相对当前路径,也可以写做“.\”)
3,“../”表示当前目录的上一层目录(也可以写做“..\”)
举例来说,上图中:..\..\Drivers\CMSIS\Device\ST\STM32F4xx\Include,前面两个“..\”,表示 Drivers 文件夹在当前 MDK 工程所在文件夹(MDK-ARM)的上 2 级目录下。
再举个例子,在完成头文件包含路径设置以后,在代码里面编写:#include "./SYSTEM/sys/sys.h"即表示当前头文件包含路径所指示的 4 个文件夹里面,肯定有某一个文件夹包含了:SYSTEM/sys/sys.h 的路径,实际上就是在 Drivers 文件夹下面,两者结合起来就相当于:#include "../../Drivers/SYSTEM/sys/sys.h"
这就是相对路径。它既可以减少头文件包含路径设置(即减少 MDK 配置步骤,免去频繁设置头文件包含路径的麻烦),同时又可以很方便的知道头文件具体在那个文件夹,因此我们推荐在编写代码的时候使用相对路径。
最后,我们如果使用 AC6 编译器,则在Target选项卡的 Misc Controls 处需要设置:-Wno-invalid source-encoding,避免中文编码报错,如果使用 AC5 编译器,则不需要该设置!!
4.5设置 Debug 选项卡
在魔术棒→Debug 选项卡里面,进行如图 4.6 所示设置:
在图4.6中,选择使用:ST-Link Debugger 仿真器(请根据使用的仿真器进行选择),使用 SW 模式,并设置最大时钟频率为 10Mhz,以得到最高下载速度。将仿真器和开发板连接好,并给开发板供电以后,仿真器就会找到开发板芯片,并在 SW Device 窗口显示芯片的 IDCODE、Device Name 等信息(图中⑤处),当无法找到时,请检查供电和仿真器连接状况。
4.6设置 Utilities 选项卡
在魔术棒→Utilities 选项卡里面,进行如图 4.7 所示设置:
注意:图中⑥处下载算法是 MDK 默认添加的,如果⑥处没有下载算法,则点击 Add 按钮,添加下载算法即可(算法名字和⑥处的算法名字一样)
5. 代码分析与编写
正点原子已经有包装好的RS485通信源码。在1.2小节中已经拷贝在Drivers\BSP文件夹下,并在3.6小节中添加到了工程中。
5.1 驱动源码分析
RS485 驱动相关源码包括两个文件:rs485.c 和 rs485.h。
rs485.h 中使用宏定义 485 相关的控制引脚和串口编号,如果需要使用其它的引脚或者串口,修改宏和串口的定义即可,它们在 rs485.h 中定义,它们列出如下:
/******************************************************************************************/
/* RS485 引脚 和 串口 定义 * 默认是针对RS4852的.* 注意: 通过修改这10个宏定义, 可以支持UART1~UART7任意一个串口.*/
#define RS485_RE_GPIO_PORT GPIOG
#define RS485_RE_GPIO_PIN GPIO_PIN_8
#define RS485_RE_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOG_CLK_ENABLE(); }while(0) /* PD口时钟使能 */#define RS485_TX_GPIO_PORT GPIOA
#define RS485_TX_GPIO_PIN GPIO_PIN_2
#define RS485_TX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */#define RS485_RX_GPIO_PORT GPIOA
#define RS485_RX_GPIO_PIN GPIO_PIN_3
#define RS485_RX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */#define RS485_UX USART2
#define RS485_UX_IRQn USART2_IRQn
#define RS485_UX_IRQHandler USART2_IRQHandler
#define RS485_UX_CLK_ENABLE() do{ __HAL_RCC_USART2_CLK_ENABLE(); }while(0) /* USART2 时钟使能 *//******************************************************************************************//* 控制RS485_RE脚, 控制RS485发送/接收状态* RS485_RE = 0, 进入接收模式* RS485_RE = 1, 进入发送模式*/
#define RS485_RE(x) do{ x ? \HAL_GPIO_WritePin(RS485_RE_GPIO_PORT, RS485_RE_GPIO_PIN, GPIO_PIN_SET) : \HAL_GPIO_WritePin(RS485_RE_GPIO_PORT, RS485_RE_GPIO_PIN, GPIO_PIN_RESET); \}while(0)
5.1.1 rs485_init 函数
rs485_init 的配置与串口类似,也需要设置波特率等参数,另外还需要配置收发模式的驱动引脚,程序设计如下:
/*** @brief RS485初始化函数* @note 该函数主要是初始化串口* @param baudrate: 波特率, 根据自己需要设置波特率值* @retval 无*/
void rs485_init(uint32_t baudrate)
{/* IO 及 时钟配置 */RS485_RE_GPIO_CLK_ENABLE(); /* 使能 RS485_RE 脚时钟 */RS485_TX_GPIO_CLK_ENABLE(); /* 使能 串口TX脚 时钟 */RS485_RX_GPIO_CLK_ENABLE(); /* 使能 串口RX脚 时钟 */RS485_UX_CLK_ENABLE(); /* 使能 串口 时钟 */GPIO_InitTypeDef gpio_initure;gpio_initure.Pin = RS485_TX_GPIO_PIN; /* 设置 TX 引脚 */gpio_initure.Mode = GPIO_MODE_AF_PP; /* 设置引脚模式为复用推挽输出 */gpio_initure.Pull = GPIO_PULLUP; /* 启用上拉电阻,保证在空闲状态下引脚为高电平 */gpio_initure.Speed = GPIO_SPEED_FREQ_HIGH; /* 设置引脚速度为高频率,保证信号稳定 */gpio_initure.Alternate = GPIO_AF7_USART2; /* 将引脚复用为串口 2 (USART2) */HAL_GPIO_Init(RS485_TX_GPIO_PORT, &gpio_initure); /* 串口TX 脚 模式设置 */gpio_initure.Pin = RS485_RX_GPIO_PIN; /* 设置 RX 引脚 */HAL_GPIO_Init(RS485_RX_GPIO_PORT, &gpio_initure); /* 串口RX 脚 必须设置成输入模式 */gpio_initure.Pin = RS485_RE_GPIO_PIN; /* 设置 RS485_RE 引脚,用于控制发送/接收模式 */gpio_initure.Mode = GPIO_MODE_OUTPUT_PP; /* 设置为推挽输出模式 */gpio_initure.Pull = GPIO_PULLUP; /* 启用上拉电阻 */gpio_initure.Speed = GPIO_SPEED_FREQ_HIGH; /* 设置引脚速度为高频率 */HAL_GPIO_Init(RS485_RE_GPIO_PORT, &gpio_initure); /* RS485_RE 脚 模式设置 *//* USART 初始化设置 */g_rs458_handler.Instance = RS485_UX; /* 选择485对应的串口 */g_rs458_handler.Init.BaudRate = baudrate; /* 设置串口 波特率 */g_rs458_handler.Init.WordLength = UART_WORDLENGTH_8B; /* 字长为8位数据格式 */g_rs458_handler.Init.StopBits = UART_STOPBITS_1; /* 一个停止位 */g_rs458_handler.Init.Parity = UART_PARITY_NONE; /* 无奇偶校验位 */g_rs458_handler.Init.HwFlowCtl = UART_HWCONTROL_NONE; /* 无硬件流控 */g_rs458_handler.Init.Mode = UART_MODE_TX_RX; /* 收发模式 */HAL_UART_Init(&g_rs458_handler); /* 使能对应的串口, 但会调用MSp */__HAL_UART_DISABLE_IT(&g_rs458_handler, UART_IT_TC);#if RS485_EN_RX /* 如果使能了接收 *//* 使能接收中断 */__HAL_UART_ENABLE_IT(&g_rs458_handler, UART_IT_RXNE); /* 开启接收中断 */HAL_NVIC_EnableIRQ(RS485_UX_IRQn); /* 使能USART1中断 */HAL_NVIC_SetPriority(RS485_UX_IRQn, 3, 3); /* 抢占优先级3,子优先级3 */
#endifRS485_RE(0); /* 默认为接收模式 */
}
可以看到代码基本跟串口的配置一样,只是多了收发控制引脚的配置。
5.1.2发送函数
发送函数用于输出 485 信号到 485 总线上,默认的 485 方式一般空闲时为接收状态,只有发送数据时485 芯片才进入发送状态,发送完成后马上回到空闲接收状态,这样可以保证操作过程中 485 的数据丢失最小。实现的发送函数如下:
/*** @brief RS485发送len个字节* @param buf : 发送区首地址* @param len : 发送的字节数(为了和本代码的接收匹配,这里建议不要超过 RS485_REC_LEN 个字节)* @retval 无*/
void rs485_send_data(uint8_t *buf, uint8_t len)
{RS485_RE(1); /* 进入发送模式 */HAL_UART_Transmit(&g_rs458_handler, buf, len, 1000); /* 串口2发送数据 */g_RS485_rx_cnt = 0;RS485_RE(0); /* 进入接收模式 */
}
5.1.3 485 接收中断函数
RS485 的接收就与串口中断一样,不过要注意空闲时要切换回接收状态,否则会收不到数据。这里定义了一个全局的缓冲区 g_RS485_rx_buf 进行接收测试,通过串口中断接收数据,编写的接收代码如下:
uint8_t g_RS485_rx_buf[RS485_REC_LEN]; /* 接收缓冲, 最大 RS485_REC_LEN 个字节. */
uint8_t g_RS485_rx_cnt = 0; /* 接收到的数据长度 */void RS485_UX_IRQHandler(void)
{uint8_t res;if ((__HAL_UART_GET_FLAG(&g_rs458_handler, UART_FLAG_RXNE) != RESET)) /* 接收到数据 */{HAL_UART_Receive(&g_rs458_handler, &res, 1, 1000);if (g_RS485_rx_cnt < RS485_REC_LEN) /* 缓冲区未满 */{g_RS485_rx_buf[g_RS485_rx_cnt] = res; /* 记录接收到的值 */g_RS485_rx_cnt++; /* 接收数据增加1 */}}
}
5.1.4 485 查询接收数据函数
该函数用于查询 485 总线上接收到的数据,主要实现的逻辑是:一开始进入函数时,先记录下当前接收计数器的值,再来一个延时去判断接收是否结束(即该期间有无接收到数据),假如说接收计数器的值没有改变,就证明接收结束,我们就可以把当前接收缓冲区传递出去。函数实现如下:
/*** @brief RS485查询接收到的数据* @param buf : 接收缓冲区首地址* @param len : 接收到的数据长度* @arg 0 , 表示没有接收到任何数据* @arg 其他, 表示接收到的数据长度* @retval 无*/
void rs485_receive_data(uint8_t *buf, uint8_t *len)
{uint8_t rxlen = g_RS485_rx_cnt;uint8_t i = 0;*len = 0; /* 默认为0 */delay_ms(10); /* 等待10ms,连续超过10ms没有接收到一个数据,则认为接收结束 */if (rxlen == g_RS485_rx_cnt && rxlen) /* 接收到了数据,且接收完成了 */{for (i = 0; i < rxlen; i++){buf[i] = g_RS485_rx_buf[i];}*len = g_RS485_rx_cnt; /* 记录本次数据长度 */g_RS485_rx_cnt = 0; /* 清零 */}
}
5.2 添加 main.c,并编写代码
5.2.1 在 MDK 主界面,点击: ,新建一个txt文件。
5.2.2 点击保存,命名为main.c 并保存在 User 文件夹下,如图 5.2 所示。
5.2.3 参考第3节添加文件步骤,双击 User 分组,弹出添加文件的对话框,将 User 文件夹下的 main.c 文件添加到 User 分组下。得到 如图 5.3 所示的界面:
5.2.4程序流程图
在 main.c 文件里面输入如下代码:
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./USMART/usmart.h"
#include "./BSP/KEY/key.h"
#include "./BSP/RS485/rs485.h"int main(void)
{uint8_t key;uint8_t i = 0, t = 0;uint8_t cnt = 0;uint8_t rs485buf[5];HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */delay_init(168); /* 延时初始化 */usart_init(115200); /* 串口初始化为115200 */usmart_dev.init(84); /* 初始化USMART */led_init(); /* 初始化LED */lcd_init(); /* 初始化LCD */key_init(); /* 初始化按键 */rs485_init(9600); /* 初始化RS485 */lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);lcd_show_string(30, 70, 200, 16, 16, "RS485 TEST", RED);lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);lcd_show_string(30, 110, 200, 16, 16, "KEY0:Send", RED); /* 显示提示信息 */lcd_show_string(30, 130, 200, 16, 16, "Count:", RED); /* 显示当前计数值 */lcd_show_string(30, 150, 200, 16, 16, "Send Data:", RED); /* 提示发送的数据 */lcd_show_string(30, 190, 200, 16, 16, "Receive Data:", RED);/* 提示接收到的数据 */while (1){key = key_scan(0);if (key == KEY0_PRES) /* KEY0按下,发送一次数据 */{for (i = 0; i < 5; i++){rs485buf[i] = cnt + i; /* 填充发送缓冲区 */lcd_show_xnum(30 + i * 32, 170, rs485buf[i], 3, 16, 0X80, BLUE); /* 显示数据 */}rs485_send_data(rs485buf, 5); /* 发送5个字节 */}rs485_receive_data(rs485buf, &key);if (key) /* 接收到有数据 */{if (key > 5)key = 5; /* 最大是5个数据. */for (i = 0; i < key; i++){lcd_show_xnum(30 + i * 32, 210, rs485buf[i], 3, 16, 0X80, BLUE); /* 显示数据 */}}t++;delay_ms(10);if (t == 20){LED0_TOGGLE(); /* LED0闪烁, 提示系统正在运行 */t = 0;cnt++;lcd_show_xnum(30 + 48, 130, cnt, 3, 16, 0X80, BLUE); /* 显示数据 */}}
}
5.2.5 编译结果 0 错误 0 警告,即编译成功。
编译结果提示:代码总大小(Porgram Size)为:FLASH 占用 44,864 字节(Code + RO + RW),SRAM 占用 2,128 字节(RW + ZI);并成功创建了 Hex 文件(可执行文件,放在 Output 目录下)。
注意:如果编译提示有错误/警告,请根据提示,从第一个错误/警告开始解决,直到 0 错误0 警告。如果出错,很有可能是之前的操作存在问题,请对照教程找问题。
6. 下载验证
连接开发板,在 MDK 主界面,点击:(下载按钮,也可以按键盘快捷键:F8),就可以将代码下载到开发板,Build Output 提示:Application running…,则表示代码下载成功,且开始运行。
6.1 验证方式一
使用一个STM32开发板和USB 转 485 调试器+串口助手,A接A,B接B。
6.2 验证方式二
通过下载代码到正点原子探索者 STM32F407 开发板上(注意要 2 个开发板都下载这个代码)。
使用 STM32F407 的串口 2 来实现两块开发板之间的 485 通信,并将结果显示在 TFTLCD 模块上。 A连A,B连B。
开发板下载成功后,显示屏显示内容如图所示:
伴随 DS0 的不停闪烁,提示程序在运行。此时,按下 KEY0 就可以在另外一个开发板上面收到这个开发板发送的数据了。
图 6.6 来自开发板 A,发送了 5 个数据,图 6.7 来自开发板 B,接收到了来自开发板A 的 5 个数据。
相关文章:
使用 STM32F407 串口实现 485 通信
准备工作 了解485通信基本概念与原理:RS485通信详解_485通讯de接什么口-CSDN博客 安装编译软件:keil uVision 5.6 软件资料:STM32CubeF4 固件包,正点原子RS485通信例程 参考视频:第26讲 基础篇-新建H…...
基于NERF技术重建学习笔记
NeRF(Neural Radiance Fields)是一种用于3D场景重建的神经网络模型,能够从2D图像生成逼真的3D渲染效果。它将场景表征为一个连续的5D函数,利用了体积渲染和神经网络的结合,通过学习光线穿过空间时的颜色和密度来重建场…...
webView 支持全屏播放
webView 支持全屏播放 直接上代码 public class CustomFullScreenWebViewClient extends WebChromeClient {WebView webView;Context context;/*** 视频全屏参数*/protected static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS new FrameLayout.LayoutParams(ViewG…...
QGIS之三十二DEM地形导出三维模型gltf
效果 1、准备数据 (1)dem.tif (2)dom.tif 2、qgis加载dem和dom数据 3、安装插件 插件步骤可以参考这篇文章 QGIS之二十四安装插件 安装了Qgis2threejs插件,结果...
【python爬虫】携程旅行景点游客数据分析与可视化
一.选题背景 随着旅游业的快速发展,越来越多的人选择通过互联网平台预订旅行产品,其中携程网作为国内领先的在线旅行服务提供商,拥有大量的旅游产品和用户数据。利用爬虫技术可以获取携程网上各个景点的游客数据,包括游客数量、游…...
python实现onvif协议下控制摄像头变焦,以及融合人形识别与跟踪控制
#1024程序员节 | 征文# 这两天才因为项目需要,对网络摄像头的视频采集以及实现人形识别与跟踪技术。对于onvif协议自然起先也没有任何的了解。但是购买的摄像头是SONY网络头是用在其他地方的。因为前期支持探究项目解决方案,就直接拿来做demo测试使用。 …...
【Vue】Vue3.0(十四)接口,泛型和自定义类型的概念及使用
上篇文章: 【Vue】Vue3.0(十三)中标签属性ref(加在普通标签上、加在组件标签上)、局部样式 🏡作者主页:点击! 🤖Vue专栏:点击! ⏰️创作时间&…...
【C++】红黑树万字详解(一文彻底搞懂红黑树的底层逻辑)
目录 00.引入 01.红黑树的性质 02.红黑树的定义 03.红黑树的插入 1.按照二叉搜索树的规则插入新节点 2.检测新节点插入后,是否满足红黑树的性质 1.uncle节点存在且为红色 2.uncle节点不存在 3.uncle节点存在且为黑色 04.验证红黑树 00.引入 和AVL树一样&am…...
开源FluentFTP实操,操控FTP文件
概述:通过FluentFTP库,轻松在.NET中实现FTP功能。支持判断、创建、删除文件夹,判断文件是否存在,实现上传、下载和删除文件。简便而强大的FTP操作,提升文件传输效率。 在.NET中,使用FluentFTP库可以方便地…...
论文解读 | ECCV2024 AutoEval-Video:一个用于评估大型视觉-语言模型在开放式视频问答中的自动基准测试...
点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入! 点击 阅读原文 观看作者讲解回放! 作者简介 陈修元,上海交通大学清源研究院硕士生 概述 总结来说,我们提出了一个新颖且具有挑战性的基准测试AutoEvalVideo,用于全…...
postgresql14主从同步流复制搭建
1. 如果使用docker搭建请移步 Docker 启动 PostgreSQL 主从架构:实现数据同步的高效部署指南_docker安装postgresql主从同步-CSDN博客 2. 背景 pgsql版本:PostgreSQL 14.13 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4…...
企业信息化管理中的数据集成方案:销售出库单对接
企业信息化管理中的数据集成方案:销售出库单对接 销售出库单旺店通→金蝶:高效数据集成案例分享 在企业信息化管理中,数据的高效流动和准确对接是实现业务流程自动化的关键。本文将聚焦于一个具体的系统对接集成案例:如何将旺店通…...
3.cpp基本数据类型
cpp基本数据类型 1.cpp基本数据类型 1.cpp基本数据类型 C基本数据类型和C语言的基本数据类型差不多 注意bool类型:存储真值 true 或假值 false,C语言编译器C99以上支持。 C语言的bool类型:要添加 #include <stdbool.h>头文件 #includ…...
MCK主机加固与防漏扫的深度解析
在当今这个信息化飞速发展的时代,网络安全成为了企业不可忽视的重要议题。漏洞扫描,简称漏扫,是一种旨在发现计算机系统、网络或应用程序中潜在安全漏洞的技术手段。通过自动化工具,漏扫能够识别出系统中存在的已知漏洞࿰…...
《软件估算之原始功能点:精准度量软件规模的关键》
《软件估算之原始功能点:精准度量软件规模的关键》 一、软件估算的重要性与方法概述二、原始功能点的构成要素(一)数据功能(二)事务功能 三、原始功能点的估算方法(一)功能点分类估算࿰…...
序列化与反序列化
序列化和反序列化是数据处理中的两个重要概念,它们在多种场景下都非常有用,尤其是在分布式系统、网络通信、持久化存储等方面。下面是对这两个概念的详细解释: 序列化(Serialization) 定义:序列化是将对象…...
安装nginx实现多ip访问多网站
[rootlocalhost ~]# systemctl stop firewalld 关防火墙 [rootlocalhost ~]# setenforce 0 关selinux [rootlocalhost ~]# mount /dev/sr0 /mnt 挂载点 [rootlocalhost ~]# dnf install nginx -y 安装nginx [rootlocalhost ~]# nmtui 当前主机添加多地址 [rootlocal…...
每日回顾:简单用C写 冒泡排序、快速排序
冒泡排序 冒泡排序(Bubble Sort)是一种简单的排序算法,它通过重复遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。遍历数列的工作是重复进行直到没有再需要交换,也就是说该数列已…...
前端_007_Axios库
文章目录 配置响应结构拦截器 引入: 官网: https://www.axios-http.cn/ 一句话简介:浏览器里基于XmlHttpRequests,node.js里基于http模块封装的网络请求库,使用非常方便 //通用例子axios({method:post,url: request…...
NAND FLASH 与 SPI FLASH
面试的时候再有HR针对从数据手册开始做,直接说明:例如RK3588等高速板设计板都有设计指导书,基本把对应的DDR等型号和布局规范都说明,或者DCDC电路直接给一个典型设计原理图,或者BMS更加经典,原理图给的是最…...
QTCreator打不开双击没反应
问题描述 双击后进程里显示有,当过几秒直接消失 解决 找到C\用户\AppData\Roaming\QtProject,删除目录下QtCreator.ini文件(这会重置QtCreator的默认设置),再打开QtCreator时会自动生成对应于默认设置的QtCreator.ini文件&…...
vue npm run ...时 报错-系统找不到指定的路径
vue项目修改时,不知道那一步操作错误了,运行npm run …时报错 系统找不到指定的路径,对此进行记录一下! 解决方法: 1、执行 npm install 命令,重新下载模块 2、根据下方提示执行 npm fund 查看详细信息 …...
54页可编辑PPT | 大型集团企业数据治理解决方案
这份PPT是关于大型集团企业数据治理的全面解决方案,它详细介绍了数据治理的背景、需求、管理范围、框架、解决思路,以及数据治理在实际操作中的关键步骤。内容涵盖了数据架构、数据质量、数据应用等方面的问题,并提出了数据资产透视、智能搜索…...
STM32嵌入式移植GmSSL库
前言 最近在做一个换电柜的项目,需要和云端平台对接json协议,由于服务端规定了,需要采用sm2 sm3 sm4用来加密。在嵌入式方面只能用北京大学的GmSSL了。 下载GmSSL 在https://github.com/guanzhi/GmSSL下载库 也可以通过git命令下载&#x…...
【mod分享】极品飞车10高清模组,,全新道路,全新建筑,高清植被,全新的道路围栏,全新的天空,画质直逼极品飞车20。支持光追
各位好,今天小编给大家带来一款新的高清重置魔改MOD,本次高清重置的游戏叫《极品飞车10卡本峡谷》。 《极品飞车10:卡本峡谷》该游戏可选择四个模式:生涯、快速比赛、挑战赛、多人连线游戏模式(已不可用)&…...
使用U-KAN训练自己的数据集 — 医疗影像分割
<U-KAN Makes Strong Backbone for Medical Image Segmentation and Generation> U-Net已成为各种视觉应用的基石,如图像分割和扩散概率模型。虽然通过整合变压器或mlp引入了许多创新设计和改进,但网络仍然局限于线性建模模式以及缺乏可解释性。为了应对这些挑战,受到…...
游戏盾在防御DDoS与CC攻击中的作用与实现
随着网络游戏的普及和发展,DDoS(分布式拒绝服务)攻击和CC(Challenge Collapsar)攻击成为了游戏服务器面临的主要威胁之一。游戏盾作为一种专门针对游戏行业设计的防御解决方案,能够在很大程度上减轻甚至消除…...
为什么说红帽认证(RHCE)是网络工程师的万金油证书?
在网络工程师圈子里,大家都知道考证的重要性,但面对一堆琳琅满目的认证,你可能会疑惑到底哪个证书含金量高、适用面广? 如果你问我,红帽认证(RHCE)绝对是当之无愧的“万金油”证书,…...
89.【C语言】编译和链接
1.翻译环境和运行环境总述 翻译环境:源代码被转换为机器码(又称为二进制指令)(包含编译和链接两个过程)依赖此环境 运行环境:可执行程序(Windows下的*.exe)到输出结果依赖此环境 2.翻译环境 翻译环境的解释 拆分为预处理(又称为预编译),编译和汇编三个过程 VS下的编译器:…...
优秀学员统计
题目描述 公司某部门软件教导团正在组织新员工每日打卡学习活动,他们开展这项学习活动已经一个月了,所以想统计下这个月优秀的打卡员工。每个员工会对应一个id,每天的打卡记录记录当天打卡员工的id集合,一共30天。 请你实现代码帮…...
360网站怎么建设/百度ai助手入口
说到删除节点,马上就会想到remove,不过原来还有一个detach,而且它们还是有区别的,就是detach保留了jquery的数据,而remove就会完全删除干净。所以如果在删除一个dom节点后还想保留它的数据以供使用就要用detach了。 jq…...
卓航网站开发/企业宣传片
心路历程: 之前学linux,虽然学的行算不错,不过总感觉差了点什么,自己找不到也说不出来;直到有一天我看到别人mount上了一个普通文件; 当时给我的感觉这太不可思议了,这个文件又不是块设备&#…...
wordpress外贸B2C建站/百度账号登录入口网页版
GCC的编译通用步骤为: 第一、在源文件目录(source)之外建立一个临时目录(build)和一个安装目录(bin)。 第二、定位到临时目录,在临时目录中调用SOURCE目录中的configure命令再在此命令后面加上你想要的参数其中必需有定…...
动态网站建设优缺点/网络推广seo公司
文章目录查看ip 在https://www.ipaddress.com中获取github.global.ssl.fastly.net和github.com的ip 添加配置 windows系统的hosts文件的位置如下:C:\Windows\System32\drivers\etc\hostsmac/linux系统的hosts文件的位置如下:/etc/hosts 140.82.113.4 git…...
wordpress迁移500/集客营销软件官方网站
原标题:只需一个python脚本就可远程控制电脑,打开微信即可远程遥控操作今天带给大家一个非常有意思的 python 程序,基于 itchat 实现微信控制电脑。你可以通过在微信发送命令,来拍摄当前电脑的使用者,然后图片会发送到…...
天津协会网站建设/网络营销推广方式包括
2019独角兽企业重金招聘Python工程师标准>>> API 包含了两个软件包,十二个接口和九个类。 软件包: javax.servlet 软件包: javax.servlet 所包含的接口:RequestDispatcher;Servlet;ServletConf…...