USART2实现Motbus485通信

本文用的单片机是原子的战舰V4

1. 先来驱动一下usart2

USART驱动配置一般步骤:

STEP1:使能相关时钟,这块板子usart2用到了A2A3分别为TX脚、RX脚,D7的作用是发送接收模式控制。下面开启GPIO与USART2时钟:

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD, ENABLE);

STEP2:初始化GPIO,A2A3D7三个IO口都要去初始化,其中TX功能IO要设置为复用推挽输出,RX功能IO要设置为浮空,D7作为发送接收控制要设为推挽输入。

/*GPIO初始化--Ck*/
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;//CK
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOD, &GPIO_InitStruct);
    /*GPIO初始化--TX*/
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;//TX
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    /*GPIO初始化--RX*/
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3; //RX
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStruct);

STEP3:接下来是USARTNVIC的初始化,串口初始化主要设置波特率、控制流、校验、停止位等,初始化完之后记得开启一下串口中断;NVIC初始化则需要设置中断通道使能、优先级等。我们默认设置为接收模式

	USART_InitStruct.USART_BaudRate = bound;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStruct.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
    USART_InitStruct.USART_Parity = USART_Parity_No;
    USART_InitStruct.USART_StopBits = USART_StopBits_1;
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    USART_Init(USART2, &USART_InitStruct);
    /*开启串口中断*/
    USART_ITConfig(USART2, USART_IT_RXNE,ENABLE); //接收中断,当接收到1个字节,会产生USART_IT_RXNE中断
    /*使能串口*/
    USART_Cmd(USART2,ENABLE);
    /*NVIC初始化*/
    NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn; //USART2通道使能
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; //子优先级
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);

STEP4:接着去写中断服务函数即可,这里先写一个简单的,下载程序后可实现“串口助手发什么就能接收到什么”。

void USART2_IRQHandler(void){
    u8 res;	    
 	if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收到数据
	{	 		 
		res =USART_ReceiveData(USART2); 
		USART_SendData(USART2,res);
	}  
}

最后不要忘记在主程序中配置中断优先级分组NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

这样我们一个简单的串口通信就完成了。

2. Motbus485通信在USART基础上又应该怎么实现

首先这边是我们的h文件,只是一些定义和声明,不详细介绍。

#ifndef __USART2_H
#define __USART2_H
#include "sys.h"

#define RS485_REC_LEN  			64  	//定义最大接收字节数 200

//模式控制
#define RS485_TX_EN		PDout(7)	//485模式控制.0,接收;1,发送.
#define EN_USART2_RX 			1		//使能(1)/禁止(0)串口1接收
	  	
extern u8  RS485_RX_BUF[RS485_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
extern u16 USART_RX_CNT;         		//接收状态标记	

void usart2_init(u32 bound);

void RS485_Receive_Data(u8 *buf,u8 *len);
void RS485_Send_Data(u8 *buf,u8 len);
#endif

中断服务函数修改:我们将接收到的数据放到一个数组中存起来。这里我们做了一个条件编译#ifdef EN_USART2_RX...#endif。可以看到相对于前面写的中断服务函数并没有什么改变,我们并没有直接将接收到的数据发出来,而实现存到一个数组里面。

#ifdef EN_USART2_RX   	//如果使能了接收
u8 RS485_RX_BUF[RS485_REC_LEN]; //定义接收缓冲200字节
u16 USART_RX_CNT=0;       //接收状态标记

void USART2_IRQHandler(void){
    u8 res;	    
 	if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收到数据
	{	 		 
		res =USART_ReceiveData(USART2); 	//读取接收到的数据
		if(USART_RX_CNT<64)
		{
			RS485_RX_BUF[USART_RX_CNT]=res;		//记录接收到的值
			USART_RX_CNT++;						//接收数据增加1 
		} 
	}  
}
#endif

接着写一下485接收程序,这边要做的事情其实就是将usart接收到的数据存到buf中。相当于是将数据从RS485_RX_BUF转到buf中,之后要做数据处理,我们直接对这个buf做数据处理即可。

void RS485_Receive_Data(u8 *buf,u8 *len){
    u8 rxlen = USART_RX_CNT;
    u8 i = 0;
    *len = 0;
    delay_ms(10);//等待10ms,连续超过10ms没有接收到一个数据,则认为接收结束
    if(rxlen == USART_RX_CNT&&rxlen){//接收到了数据,且接收完成了
        for(i = 0; i < rxlen; i++){
            buf[i] = RS485_RX_BUF[i];
        }
        *len = USART_RX_CNT;
        USART_RX_CNT = 0; //清0
    }  
}

写一下485发送数据的程序,我们要判断数据是否发完了,没有发完就用USART_SendData去发

先了解一下USART_FLAG_TC这个标志是干嘛的
当发送移位寄存器中的1字节数据已经通过TX脚一位一位的移出去后,该标志位就会被置1,从而引发该事件的中断。所以,其实USART_FLAG_TC就是用来标志 “发送移位寄存器中的数据有没有全部发送出去” 这件事的。

void RS485_Send_Data(u8 *buf,u8 len){
    u8 t;
    RS485_TX_EN=1;			//设置为发送模式
    for(t=0;t<len;t++)		//循环发送数据
	{		   
		while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);	  
		USART_SendData(USART2,buf[t]);
	}	 
	while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);		
	USART_RX_CNT=0;	  
	RS485_TX_EN=0;				//设置为接收模式	
}

我们定义了一个要发送的数据数组与数据接收数组,接着发送、接收。

u8 TR_Read[8]={0x01,0x03,0x00,0x00,0x00,0x02,0xC4,0x0B}; 
u8 rs485buf[20]={0}; 
RS485_Send_Data(TR_Read,8);
delay_ms(300);
RS485_Receive_Data(rs485buf,&len);

实验现象:接着打开串口进行测试,指令就发出来了。
在这里插入图片描述

一般情况下,我们只要向485传感器发送相关指令,就可以驱动这种传感器了。比如你要获取传感器数据,你就发送数据获取指令;需要修改地址,就发送修改地址的指令。485收发你都会了,传感器驱动哪里还是什么问题呢。

创作不易!希望收获你的点赞与关注👍🤝🤝

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是基于STM32F103的USART3串口实现RS485通信的示例代码: ``` #include "stm32f10x.h" #define USART3_DR_Base ((uint32_t)0x40004804) // 定义USART3的GPIO引脚 #define USART3_TX_GPIO_PORT GPIOB #define USART3_TX_GPIO_CLK RCC_APB2Periph_GPIOB #define USART3_TX_PIN GPIO_Pin_10 #define USART3_RX_GPIO_PORT GPIOB #define USART3_RX_GPIO_CLK RCC_APB2Periph_GPIOB #define USART3_RX_PIN GPIO_Pin_11 // 定义RS485的控制引脚 #define RS485_CTRL_GPIO_PORT GPIOB #define RS485_CTRL_GPIO_CLK RCC_APB2Periph_GPIOB #define RS485_CTRL_PIN GPIO_Pin_12 // 定义RS485的发送和接收状态 #define RS485_RX_EN() (RS485_CTRL_GPIO_PORT->BRR = RS485_CTRL_PIN) #define RS485_TX_EN() (RS485_CTRL_GPIO_PORT->BSRR = RS485_CTRL_PIN) // 定义USART3的波特率 #define USART3_BAUDRATE 9600 void USART3_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; // 使能GPIO和USART的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | USART3_TX_GPIO_CLK | USART3_RX_GPIO_CLK, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); // 配置USART3的GPIO引脚 GPIO_InitStructure.GPIO_Pin = USART3_TX_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(USART3_TX_GPIO_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = USART3_RX_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(USART3_RX_GPIO_PORT, &GPIO_InitStructure); // 配置RS485控制引脚 GPIO_InitStructure.GPIO_Pin = RS485_CTRL_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(RS485_CTRL_GPIO_PORT, &GPIO_InitStructure); // 配置USART3的参数 USART_InitStructure.USART_BaudRate = USART3_BAUDRATE; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART3, &USART_InitStructure); // 使能USART3 USART_Cmd(USART3, ENABLE); } void USART3_SendByte(uint8_t byte) { // 等待发送缓冲区为空 while (USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET); // 发送数据 USART_SendData(USART3, byte); } uint8_t USART3_ReceiveByte(void) { // 等待接收到数据 while (USART_GetFlagStatus(USART3, USART_FLAG_RXNE) == RESET); // 读取数据 return USART_ReceiveData(USART3); } void RS485_SendData(uint8_t *data, uint16_t len) { // 切换为发送状态 RS485_TX_EN(); // 逐个发送字节 for (int i = 0; i < len; i++) { USART3_SendByte(data[i]); } // 等待数据发送完成 while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET); // 切换为接收状态 RS485_RX_EN(); } void RS485_ReceiveData(uint8_t *data, uint16_t len) { // 切换为接收状态 RS485_RX_EN(); // 逐个接收字节 for (int i = 0; i < len; i++) { data[i] = USART3_ReceiveByte(); } } int main(void) { USART3_Config(); // 发送数据 uint8_t tx_data[] = {0x01, 0x02, 0x03, 0x04}; RS485_SendData(tx_data, sizeof(tx_data)); // 接收数据 uint8_t rx_data[4]; RS485_ReceiveData(rx_data, sizeof(rx_data)); while (1); } ``` 在这个示例代码中,我们首先配置了USART3的GPIO引脚和参数,然后定义了RS485的控制引脚和发送、接收状态的函数。在主函数中,我们先发送了一些数据,然后接收了一些数据。需要注意的是,在发送数据之前需要先切换为发送状态,在接收数据之前需要先切换为接收状态。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快乐大队队长

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值