当前位置: 首页 > 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 个数据。 


http://www.mrgr.cn/news/57893.html

相关文章:

  • 文理学院数据库应用技术实验报告0
  • html全局属性、框架标签
  • 达梦数据库名词介绍-----持续更新中
  • 【含开题报告+文档+PPT+源码】基于vue框架的东升餐饮点餐管理平台的设计与实现
  • webpack生成的SourceMap更改生成路径
  • uniapp_微信小程序_echarts_动态折线图
  • # TOGAF技术架构:小白也能看懂的指南
  • 【github小问题】——push后报错error: src refspec master does not match any
  • 爆肝整理14天!AI工具宝藏合集
  • 一文掌握 jetbrains IDE 新 UI,还不会新 UI 的同学快看过来
  • 还有谁不会解决农夫过河问题
  • 教程分享!超简单的低功耗4G模组LCD应用示例!看过来~
  • 掌握5个技巧,让你的答题测试小程序广告,收入提高3倍!
  • 2024_Newstar_week3_Crypto
  • Angular 控制流与延迟视图揭秘
  • [OS] Pthread 条件变量
  • 常用设计模式总结
  • VantUI
  • 大厂的JAVA经典面试题-初中级
  • 基于SpringBoot足球场在线预约系统的设计与实现
  • 海王3纯源码
  • 分享一个开源的、自托管的 API 创建工具——Strapi
  • 又是一年 1024
  • 轻松清理 PC 微信文件,释放存储空间
  • C++学习路线(二十三)
  • EureKa是什么?