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

使用 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.1 所示:

表1.1 工程根目录新建文件夹及其作用

 新建完成以后,最后得到的工程根目录文件夹如图 1.2 所示。

1.2 工程根目录文件夹

1.2拷贝工程相关文件

接下来,按根目录文件夹顺序介绍每个文件夹及其需要拷贝的文件。

1.2.1 Drivers 文件夹

该文件夹用于存放与硬件相关的驱动层文件,一般包括如表 1.2 所示的三个文件夹:

表 1.2.1 Drivers 包含文件夹

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 所示:

图 1.3 工程根目录下的 Drivers 文件夹

关于工程根目录下的 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.4 在 Projects 文件夹下新建 MDK-ARM 文件夹

1.2.5 User 文件夹

User 文件夹用于存放 HAL 库用户配置文件、main.c、中断处理文件,以及分散加载文件。

首先从官方固件包里面直接拷贝官方的模板工程下的 HAL 库用户配置文件和中断处理文件到 User 文件夹里。官方的模板工程路径:STM32Cube_FW_F4_V1.26.0\Projects\STM324xG_EVAL\Templates,打开Template_Project 文件夹,如图 1.5 所示。

图1.5 官方模板工程根目录

Inc Src 两个文件夹里面找到:stm32f4xx_it.cstm32f4xx_it.hstm32f4xx_hal_conf.h 这三个文件,并且拷贝到 User 文件夹下。

注意:main.c文件也是放在User文件夹里面的,后面需要在MDK 里面新建.c 文件并保存。

2. 新建一个工程框架

2.1 首先,打开 MDK 软件。然后点击 Project→New uVision Project 如图 2.1 所示:

图 2.1 新建 MDK 工程

2.2 然后弹出工程命名和保存的操作窗口,将工程文件保存路径设置在上一节新建的工程文件夹内,具体路径为:485TestOne→Projects→MDK-ARM,

工程名字为:atk_f407,最后点击保存即可。具体操作窗口如图 2.2所示:

图 2.2 工程命名和保存的操作窗口

2.3 之后,弹出器件选择对话框,如图2.3所示。

图2.3 器件选择界面

因为探索者 STM32F407 开发板所使用的STM32 型号为 STM32F407ZGT6,所以选择:STMicroelectronics → STM32F4 Series →STM32F407 → STM32F407ZGTx(如果使用的是其他系列的芯片,选择相应的型号就可以了,特别注意:一定要安装对应的器件 pack 才会显示这些内容哦!!如果没得选择,请关闭 MDK,安装后重试。

2.4 点击 OKMDK 会弹出 Manage Run-Time Environment 对话框,如图2.4所示:

图2.4 Manage Run-Time Environment 界面

这是 MDK5 新增的一个功能,在这个界面,可以添加自己需要的组件,从而方便构建开发环境,这里不做介绍。直接点击 Cancel,即可,

2.5 得到如图 2.5 所示界面:

图2.5 工程初步建立

2.6 此时,打开 MDK-ARM 文件夹,会看到 MDK 在该文件夹下自动创建了 3 个文件夹 (DebugConfigListings Objects),如图所示:

图2.6 MDK 新建工程时自动创建的文件夹

这三个文件夹的作用:

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 进入工程管理界面

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.2 设置工程名和分组名

3.1.3 设置好之后,点击 OK,回到 MDK 主界面,可以看到设置的工程名和分组名如图所示:

图 3.3 设置成功

注意:为了让工程结构清晰,尽量使 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所示:

图3.4 双击分组添加启动文件(startup_stm32f407xx.s)

上图中,也可以点击 Add 按钮进行文件添加。添加完后,点击 Close,完成启动文件添加,得到工程分组如图所示:

图 3.5 启动文件添加成功

3.3 添加 SYSTEM 源码

3.3.1 这里使用工程管理界面(方法 2)进行 SYSTEM 源码添加。点击: 按钮,进入工程管 理界面,选中 Drivers/SYSTEM 分组,然后点击:Add Files,进入文件添加对话框,在依次添加 delay.csys.c usart.c 到该分组下,如图 3.6 所示:

图 3.6 添加 SYSTEM 源码

注意:这些源码都是在第 1.2 小节的第二步拷贝过来的,如果之前没拷贝,是找不到这些源码的。

3.3.2 添加完成后,如图3.7所示:

图 3.7 SYSTEM 源码添加完成

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.8 User 源码添加完成

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所示:

图 3.9 STM32F4xx_HAL_Driver 源码添加完成

注意:如果看到分组中有些.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.10 BSP 源码添加完成

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 所示:

图 3.11 USMART 源码添加完成

4. 魔术棒设置

为避免编写代码和编译报错,需要通过魔术棒对 MDK 工程进行相关设置。在 MDK主界面,点击: (魔术棒图标,即 Options for Target 按钮),进入工程设置对话框,将进行如下几个选项卡的设置。

4.1 设置 Target 选项卡

图4.1 Target 选项卡设置

在图 4.1 中,设置芯片所使用的外部晶振频率为 8Mhz(uVision 5.36及以上版本是灰色,不用设置),选择 ARM Compiler 版本为:Use default compiler version 5(即 AC5 编译器)。

AC5 和 AC6 编译的差异:

表 4.1 AC5&AC6 简单对比

4.2 设置 Output 选项卡

在魔术棒→Output 选项卡里面,进行如图 4.2 所示设置

图 4.2 设置 Output 选项卡

注意:选:Browse Information,可用于输出浏览信息,就能使用 go to definition查看函数/变量的定义,对后续调试代码比较有帮助,如果不需要调试代码,则可以去掉勾选,以提高编译速度。

4.3 设置 Listing 选项卡

在魔术棒→Listing 选项卡里面,进行如图 4.3 所示设置:

图 4.3 设置 Listing 选项卡

经过 Output 和 Listing 这两步设置,原来存储在 Objects 和 Listings 文件夹的内容(中间文件)就都改为输出到 Output 文件夹了。

4.4 设置 C/C++选项卡

在魔术棒→C/C++选项卡里面,进行如图 4.4 所示设置:

图 4.4 设置 C/C++选项卡

处设置了全局宏定义:USE_HAL_DRIVER和STM32F407xx,他们之间用英文逗号隔开。STM32F407xx用于定义所用 STM32 型号,在 stm32f4xx.h 里面会用到该宏定义。

处设置了优化等级为-O0,可以得到最好的调试效果,当然为了提高优化效果提升性能并降低代码量,可以设置-O1~-O3,数字越大效果越明显,不过也越容易出问题。注意:当使用AC6 编译器的时候,这里推荐默认使用-O1 优化。

处勾选 C99 模式,即使用 C99 C 语言标准。

处,进行头文件包含路径设置,点击此按钮,进行如图 所示设置:

图 4.5 设置头文件包含路径

上图中设置了 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 Debug 选项卡设置

在图4.6中,选择使用:ST-Link Debugger 仿真器(请根据使用的仿真器进行选择),使用 SW 模式,并设置最大时钟频率为 10Mhz,以得到最高下载速度。将仿真器和开发板连接好,并给开发板供电以后,仿真器就会找到开发板芯片,并在 SW Device 窗口显示芯片的 IDCODE、Device Name 等信息(图中处),当无法找到时,请检查供电和仿真器连接状况。

4.6设置 Utilities 选项卡

在魔术棒→Utilities 选项卡里面,进行如图 4.7 所示设置:

图 4.7 Utilities 选项卡设置

注意:图中处下载算法是 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.1 新建文件

5.2.2 点击保存,命名为main.c 并保存在 User 文件夹下,如图 5.2 所示。

图 5.2 重命名并保存main.c

5.2.3 参考第3节添加文件步骤,双击 User 分组,弹出添加文件的对话框,将 User 文件夹下的 main.c 文件添加到 User 分组下。得到 如图 5.3 所示的界面:

图 5.3 在 User 分组下加入 main.c 文件

5.2.4程序流程图

图 5.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 目录下)。

图 5.5 运行成功结果

注意:如果编译提示有错误/警告,请根据提示,从第一个错误/警告开始解决,直到 0 错误0 警告。如果出错,很有可能是之前的操作存在问题,请对照教程找问题。

6. 下载验证

连接开发板,在 MDK 主界面,点击:(下载按钮,也可以按键盘快捷键:F8),就可以将代码下载到开发板,Build Output 提示:Application running…,则表示代码下载成功,且开始运行。

图 6.1 下载成功

6.1 验证方式一

使用一个STM32开发板和USB 485 调试器+串口助手,A接A,B接B。

图 6.2 STM32开发板和USB 转 485 调试器
图 6.3 串口调试助手收发结果(16进制显示)

6.2 验证方式二

通过下载代码到正点原子探索者 STM32F407 开发板上(注意要 2 个开发板都下载这个代码)。

使用 STM32F407 的串口 2 来实现两块开发板之间的 485 通信,并将结果显示在 TFTLCD 模块上。 A连A,B连B。

图 6.4 两块开发板

 开发板下载成功后,显示屏显示内容如图所示:

图 6.5 程序运行效果图

伴随 DS0 的不停闪烁,提示程序在运行。此时,按下 KEY0 就可以在另外一个开发板上面收到这个开发板发送的数据了。

图 6.6 发送 RS485 数据的开发板界面
图 6.7 接收 RS485 数据的开发板界面

图 6.6 来自开发板 A,发送了 5 个数据,图 6.7 来自开发板 B,接收到了来自开发板A 的 5 个数据。 

相关文章:

使用 STM32F407 串口实现 485 通信

准备工作 了解485通信基本概念与原理&#xff1a;RS485通信详解_485通讯de接什么口-CSDN博客 安装编译软件&#xff1a;keil uVision 5.6 软件资料&#xff1a;STM32CubeF4 固件包&#xff0c;正点原子RS485通信例程 ​​​​​​​参考视频&#xff1a;第26讲 基础篇-新建H…...

基于NERF技术重建学习笔记

NeRF&#xff08;Neural Radiance Fields&#xff09;是一种用于3D场景重建的神经网络模型&#xff0c;能够从2D图像生成逼真的3D渲染效果。它将场景表征为一个连续的5D函数&#xff0c;利用了体积渲染和神经网络的结合&#xff0c;通过学习光线穿过空间时的颜色和密度来重建场…...

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爬虫】携程旅行景点游客数据分析与可视化

一.选题背景 随着旅游业的快速发展&#xff0c;越来越多的人选择通过互联网平台预订旅行产品&#xff0c;其中携程网作为国内领先的在线旅行服务提供商&#xff0c;拥有大量的旅游产品和用户数据。利用爬虫技术可以获取携程网上各个景点的游客数据&#xff0c;包括游客数量、游…...

python实现onvif协议下控制摄像头变焦,以及融合人形识别与跟踪控制

#1024程序员节 | 征文# 这两天才因为项目需要&#xff0c;对网络摄像头的视频采集以及实现人形识别与跟踪技术。对于onvif协议自然起先也没有任何的了解。但是购买的摄像头是SONY网络头是用在其他地方的。因为前期支持探究项目解决方案&#xff0c;就直接拿来做demo测试使用。 …...

【Vue】Vue3.0(十四)接口,泛型和自定义类型的概念及使用

上篇文章&#xff1a; 【Vue】Vue3.0&#xff08;十三&#xff09;中标签属性ref&#xff08;加在普通标签上、加在组件标签上&#xff09;、局部样式 &#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;Vue专栏&#xff1a;点击&#xff01; ⏰️创作时间&…...

【C++】红黑树万字详解(一文彻底搞懂红黑树的底层逻辑)

目录 00.引入 01.红黑树的性质 02.红黑树的定义 03.红黑树的插入 1.按照二叉搜索树的规则插入新节点 2.检测新节点插入后&#xff0c;是否满足红黑树的性质 1.uncle节点存在且为红色 2.uncle节点不存在 3.uncle节点存在且为黑色 04.验证红黑树 00.引入 和AVL树一样&am…...

开源FluentFTP实操,操控FTP文件

概述&#xff1a;通过FluentFTP库&#xff0c;轻松在.NET中实现FTP功能。支持判断、创建、删除文件夹&#xff0c;判断文件是否存在&#xff0c;实现上传、下载和删除文件。简便而强大的FTP操作&#xff0c;提升文件传输效率。 在.NET中&#xff0c;使用FluentFTP库可以方便地…...

论文解读 | ECCV2024 AutoEval-Video:一个用于评估大型视觉-语言模型在开放式视频问答中的自动基准测试...

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 点击 阅读原文 观看作者讲解回放&#xff01; 作者简介 陈修元&#xff0c;上海交通大学清源研究院硕士生 概述 总结来说&#xff0c;我们提出了一个新颖且具有挑战性的基准测试AutoEvalVideo&#xff0c;用于全…...

postgresql14主从同步流复制搭建

1. 如果使用docker搭建请移步 Docker 启动 PostgreSQL 主从架构&#xff1a;实现数据同步的高效部署指南_docker安装postgresql主从同步-CSDN博客 2. 背景 pgsql版本&#xff1a;PostgreSQL 14.13 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4…...

企业信息化管理中的数据集成方案:销售出库单对接

企业信息化管理中的数据集成方案&#xff1a;销售出库单对接 销售出库单旺店通→金蝶&#xff1a;高效数据集成案例分享 在企业信息化管理中&#xff0c;数据的高效流动和准确对接是实现业务流程自动化的关键。本文将聚焦于一个具体的系统对接集成案例&#xff1a;如何将旺店通…...

3.cpp基本数据类型

cpp基本数据类型 1.cpp基本数据类型 1.cpp基本数据类型 C基本数据类型和C语言的基本数据类型差不多 注意bool类型&#xff1a;存储真值 true 或假值 false&#xff0c;C语言编译器C99以上支持。 C语言的bool类型&#xff1a;要添加 #include <stdbool.h>头文件 #includ…...

MCK主机加固与防漏扫的深度解析

在当今这个信息化飞速发展的时代&#xff0c;网络安全成为了企业不可忽视的重要议题。漏洞扫描&#xff0c;简称漏扫&#xff0c;是一种旨在发现计算机系统、网络或应用程序中潜在安全漏洞的技术手段。通过自动化工具&#xff0c;漏扫能够识别出系统中存在的已知漏洞&#xff0…...

《软件估算之原始功能点:精准度量软件规模的关键》

《软件估算之原始功能点&#xff1a;精准度量软件规模的关键》 一、软件估算的重要性与方法概述二、原始功能点的构成要素&#xff08;一&#xff09;数据功能&#xff08;二&#xff09;事务功能 三、原始功能点的估算方法&#xff08;一&#xff09;功能点分类估算&#xff0…...

序列化与反序列化

序列化和反序列化是数据处理中的两个重要概念&#xff0c;它们在多种场景下都非常有用&#xff0c;尤其是在分布式系统、网络通信、持久化存储等方面。下面是对这两个概念的详细解释&#xff1a; 序列化&#xff08;Serialization&#xff09; 定义&#xff1a;序列化是将对象…...

安装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写 冒泡排序、快速排序

冒泡排序 冒泡排序&#xff08;Bubble Sort&#xff09;是一种简单的排序算法&#xff0c;它通过重复遍历要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果他们的顺序错误就把他们交换过来。遍历数列的工作是重复进行直到没有再需要交换&#xff0c;也就是说该数列已…...

前端_007_Axios库

文章目录 配置响应结构拦截器 引入&#xff1a; 官网&#xff1a; https://www.axios-http.cn/ 一句话简介&#xff1a;浏览器里基于XmlHttpRequests&#xff0c;node.js里基于http模块封装的网络请求库&#xff0c;使用非常方便 //通用例子axios({method:post,url: request…...

NAND FLASH 与 SPI FLASH

面试的时候再有HR针对从数据手册开始做&#xff0c;直接说明&#xff1a;例如RK3588等高速板设计板都有设计指导书&#xff0c;基本把对应的DDR等型号和布局规范都说明&#xff0c;或者DCDC电路直接给一个典型设计原理图&#xff0c;或者BMS更加经典&#xff0c;原理图给的是最…...

QTCreator打不开双击没反应

问题描述 双击后进程里显示有,当过几秒直接消失 解决 找到C\用户\AppData\Roaming\QtProject&#xff0c;删除目录下QtCreator.ini文件&#xff08;这会重置QtCreator的默认设置&#xff09;&#xff0c;再打开QtCreator时会自动生成对应于默认设置的QtCreator.ini文件&…...

vue npm run ...时 报错-系统找不到指定的路径

vue项目修改时&#xff0c;不知道那一步操作错误了&#xff0c;运行npm run …时报错 系统找不到指定的路径&#xff0c;对此进行记录一下&#xff01; 解决方法&#xff1a; 1、执行 npm install 命令&#xff0c;重新下载模块 2、根据下方提示执行 npm fund 查看详细信息 …...

54页可编辑PPT | 大型集团企业数据治理解决方案

这份PPT是关于大型集团企业数据治理的全面解决方案&#xff0c;它详细介绍了数据治理的背景、需求、管理范围、框架、解决思路&#xff0c;以及数据治理在实际操作中的关键步骤。内容涵盖了数据架构、数据质量、数据应用等方面的问题&#xff0c;并提出了数据资产透视、智能搜索…...

STM32嵌入式移植GmSSL库

前言 最近在做一个换电柜的项目&#xff0c;需要和云端平台对接json协议&#xff0c;由于服务端规定了&#xff0c;需要采用sm2 sm3 sm4用来加密。在嵌入式方面只能用北京大学的GmSSL了。 下载GmSSL 在https://github.com/guanzhi/GmSSL下载库 也可以通过git命令下载&#x…...

【mod分享】极品飞车10高清模组,,全新道路,全新建筑,高清植被,全新的道路围栏,全新的天空,画质直逼极品飞车20。支持光追

各位好&#xff0c;今天小编给大家带来一款新的高清重置魔改MOD&#xff0c;本次高清重置的游戏叫《极品飞车10卡本峡谷》。 《极品飞车10&#xff1a;卡本峡谷》该游戏可选择四个模式&#xff1a;生涯、快速比赛、挑战赛、多人连线游戏模式&#xff08;已不可用&#xff09;&…...

使用U-KAN训练自己的数据集 — 医疗影像分割

<U-KAN Makes Strong Backbone for Medical Image Segmentation and Generation> U-Net已成为各种视觉应用的基石,如图像分割和扩散概率模型。虽然通过整合变压器或mlp引入了许多创新设计和改进,但网络仍然局限于线性建模模式以及缺乏可解释性。为了应对这些挑战,受到…...

游戏盾在防御DDoS与CC攻击中的作用与实现

随着网络游戏的普及和发展&#xff0c;DDoS&#xff08;分布式拒绝服务&#xff09;攻击和CC&#xff08;Challenge Collapsar&#xff09;攻击成为了游戏服务器面临的主要威胁之一。游戏盾作为一种专门针对游戏行业设计的防御解决方案&#xff0c;能够在很大程度上减轻甚至消除…...

为什么说红帽认证(RHCE)是网络工程师的万金油证书?

在网络工程师圈子里&#xff0c;大家都知道考证的重要性&#xff0c;但面对一堆琳琅满目的认证&#xff0c;你可能会疑惑到底哪个证书含金量高、适用面广&#xff1f; 如果你问我&#xff0c;红帽认证&#xff08;RHCE&#xff09;绝对是当之无愧的“万金油”证书&#xff0c;…...

89.【C语言】编译和链接

1.翻译环境和运行环境总述 翻译环境:源代码被转换为机器码(又称为二进制指令)(包含编译和链接两个过程)依赖此环境 运行环境:可执行程序(Windows下的*.exe)到输出结果依赖此环境 2.翻译环境 翻译环境的解释 拆分为预处理(又称为预编译),编译和汇编三个过程 VS下的编译器:…...

优秀学员统计

题目描述 公司某部门软件教导团正在组织新员工每日打卡学习活动&#xff0c;他们开展这项学习活动已经一个月了&#xff0c;所以想统计下这个月优秀的打卡员工。每个员工会对应一个id&#xff0c;每天的打卡记录记录当天打卡员工的id集合&#xff0c;一共30天。 请你实现代码帮…...

360网站怎么建设/百度ai助手入口

说到删除节点&#xff0c;马上就会想到remove&#xff0c;不过原来还有一个detach&#xff0c;而且它们还是有区别的&#xff0c;就是detach保留了jquery的数据&#xff0c;而remove就会完全删除干净。所以如果在删除一个dom节点后还想保留它的数据以供使用就要用detach了。 jq…...

卓航网站开发/企业宣传片

心路历程&#xff1a; 之前学linux&#xff0c;虽然学的行算不错&#xff0c;不过总感觉差了点什么&#xff0c;自己找不到也说不出来&#xff1b;直到有一天我看到别人mount上了一个普通文件&#xff1b; 当时给我的感觉这太不可思议了&#xff0c;这个文件又不是块设备&#…...

wordpress外贸B2C建站/百度账号登录入口网页版

GCC的编译通用步骤为: 第一、在源文件目录(source)之外建立一个临时目录&#xff08;build&#xff09;和一个安装目录&#xff08;bin&#xff09;。 第二、定位到临时目录&#xff0c;在临时目录中调用SOURCE目录中的configure命令再在此命令后面加上你想要的参数其中必需有定…...

动态网站建设优缺点/网络推广seo公司

文章目录查看ip 在https://www.ipaddress.com中获取github.global.ssl.fastly.net和github.com的ip 添加配置 windows系统的hosts文件的位置如下&#xff1a;C:\Windows\System32\drivers\etc\hostsmac/linux系统的hosts文件的位置如下&#xff1a;/etc/hosts 140.82.113.4 git…...

wordpress迁移500/集客营销软件官方网站

原标题&#xff1a;只需一个python脚本就可远程控制电脑&#xff0c;打开微信即可远程遥控操作今天带给大家一个非常有意思的 python 程序&#xff0c;基于 itchat 实现微信控制电脑。你可以通过在微信发送命令&#xff0c;来拍摄当前电脑的使用者&#xff0c;然后图片会发送到…...

天津协会网站建设/网络营销推广方式包括

2019独角兽企业重金招聘Python工程师标准>>> API 包含了两个软件包&#xff0c;十二个接口和九个类。 软件包&#xff1a; javax.servlet 软件包&#xff1a; javax.servlet 所包含的接口&#xff1a;RequestDispatcher&#xff1b;Servlet&#xff1b;ServletConf…...