STM32+rs422单设备通信

本文讲述了使用STM32F103微控制器配合串口转422模块进行RS485通信的实验过程,介绍了上位机和下位机的代码实现,以及接线注意事项。实验仅实现单向数据传输,后续将扩展为双向通信。
摘要由CSDN通过智能技术生成

  流放吉林做项目,项目需求中需要用RS485通信协议,又因为买的串口转485模块没到,就先拿422的模块实验一下;                        本人也是初次接触,仅实现了数据传输的功能,欢迎大佬指正;

实验设备

上位机:STM32F103VET6

下位机:STM32F103ZET6

串口转422模块:

CH340模块:

接线部分

上位机和下位机都是使用的UART1的资源来进行收发的;

上位机代码部分

main.c代码部分:

#include "stm32f10x.h"                  // Device header
#include <DELAY.H>
#include "adc.h"
#include <LED.h>
#include <UART.H>
#include <SERIAL2.H>

float dat=0.00;
uint16_t temp = 0;

uint8_t RxData;


int main(void)
{
	LED_INIT();
	adc_init();
	Serial_Init();
	
	Serial_Printf("system init end\r\n");
	
	while(1)
	{
	
		if (Serial_GetRxFlag() == 1)//串口接受标志位
		{
			RxData = Serial_GetRxData();
			Serial_SendByte(RxData);
			//Serial2_Printf("接收:%d \r\n",RxData);
		}
		
		Delay_ms(1000);
	}
	
}

此处的逻辑是,当我们检测到串口1的接收标志位置1之后,直接将接收的数据通过TX引脚发送出去;

USART.C代码:

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>

uint8_t Serial_RxData;
uint8_t Serial_RxFlag;

void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART1, &USART_InitStructure);
	
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
	USART_Cmd(USART1, ENABLE);
}

void Serial_SendByte(uint8_t Byte)
{
	USART_SendData(USART1, Byte);
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Array[i]);
	}
}

void Serial_SendString(char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)
	{
		Serial_SendByte(String[i]);
	}
}

uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y --)
	{
		Result *= X;
	}
	return Result;
}

void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');
	}
}

int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}

void Serial_Printf(char *format, ...)
{
	char String[100];
	va_list arg;
	va_start(arg, format);
	vsprintf(String, format, arg);
	va_end(arg);
	Serial_SendString(String);
}

uint8_t Serial_GetRxFlag(void)
{
	if (Serial_RxFlag == 1)
	{
		Serial_RxFlag = 0;
		return 1;
	}
	return 0;
}

uint8_t Serial_GetRxData(void)
{
	return Serial_RxData;
}

void USART1_IRQHandler(void)
{
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
	{
		Serial_RxData = USART_ReceiveData(USART1);
		Serial_RxFlag = 1;
		//Serial_SendByte(Serial_RxData);
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}




这里对于UART1的配置为常用的N81,就是无校验位、8个数据位、一个停止位、波特率为9600;还有对于串口中断和串口输出的配置;

USART.h代码:

#ifndef __SERIAL_H
#define __SERIAL_H

#include <stdio.h>

void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);

uint8_t Serial_GetRxFlag(void);
uint8_t Serial_GetRxData(void);

#endif


下位机代码部分:

下位机的代码和上位机类似,主要区别是上位机接收数据,下位机发送数据,发送数据一直在进行;

main.c代码:

#include "stm32f10x.h"                  // Device header
#include <DELAY.H>
#include <USART.H>
#include "adc.h"
#include "key.h"


float dat=32.614;
uint16_t temp = 0;

uint32_t test = 32614;

u8 KEY_val;

uint8_t byte_buff[5]={3,2,6,1,4};

u8 RxData;

int main(void)
{
	adc_init();
	Serial_Init();
	KEY_Init();
	LED_Init();
	
	Serial_Printf("system init end\r\n");
	
	while(1)
	{
		//Serial_SendArray(byte_buff,5);
		//Serial_SendNumber(test,5);
			Serial_SendByte(9);

		Delay_ms(1000);
	}
	
}

在while循环里可选择我们发送的数据类型,主要是看serial函数的类型;

USART.C代码:

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>

uint8_t Serial_RxData;
uint8_t Serial_RxFlag;

void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART1, &USART_InitStructure);
	
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
	USART_Cmd(USART1, ENABLE);
}

void Serial_SendByte(uint8_t Byte)
{
	USART_SendData(USART1, Byte);
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Array[i]);
	}
}

void Serial_SendString(char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)
	{
		Serial_SendByte(String[i]);
	}
}

uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y --)
	{
		Result *= X;
	}
	return Result;
}

void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');
	}
}

int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}

void Serial_Printf(char *format, ...)
{
	char String[100];
	va_list arg;
	va_start(arg, format);
	vsprintf(String, format, arg);
	va_end(arg);
	Serial_SendString(String);
}

uint8_t Serial_GetRxFlag(void)
{
	if (Serial_RxFlag == 1)
	{
		Serial_RxFlag = 0;
		return 1;
	}
	return 0;
}

uint8_t Serial_GetRxData(void)
{
	return Serial_RxData;
}

void USART1_IRQHandler(void)
{
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
	{
		Serial_RxData = USART_ReceiveData(USART1);
		Serial_RxFlag = 1;
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}




USART.h代码:

#ifndef UART_H_
#define UART_H_

#include "stm32f10x.h"  


#include <stdio.h>

void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);

uint8_t Serial_GetRxFlag(void);
uint8_t Serial_GetRxData(void);

#endif


注意事项:

注意看接线部分,给422模块供电我是直接用的单片机上的3.3v进行供电的;

主要考虑两点,第一点是:如果直接给5V供电会不会对单片机有影响甚至烧坏;

第二点是,422的标准其实是物理层的也可以说是电气协议,是因为,在长距离通信的过程中直接用串口通信是不能够完成通信的;而我们在做实验的过程中接线距离很短,所以说3.3v已经够用了;

在本实验中我只完成了下位机发数据给上位机,上位机将数据发送给串口助手在电脑上显示;这个通信是单向的,只完成了下位机向上位机传输数据;(在本实验中,其实也可以说没有上位机和下位机,但是后面会在此基础之上,完成双向通信加上一些操作,才是真正的上位机和下位机;会面会继续更新);

最重要的一点,就是接线的问题,参考界限部分的图片和下图;

在ch340模块那里:串口的tx接ch340的rx;而单片机接422模块的时候就是,rx接rx、tx接tx;

还有,在422模块之间的接线如上图所示:A接Y、B接Z、Z接B、Y接A;     (电源线和地线一定不要接反了,轻则芯片烧坏,还有可能烫伤自己)!!!!

实验现象

完整工程:

链接:链接:https://pan.baidu.com/s/1unAzQCu_NSIuSoq6qmfTNg 
提取码:xq21

  • 22
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32是一系列由STMicroelectronics公司生产的32位微控制器。RS485是一种串行通信协议,用于在多个设备之间进行通信,通常用于远程数据采集、监控和控制领域。在STM32中使用RS485通信需要实现相应的通信协议。 以下是基于HAL库的STM32+RS485通讯协议代码详解: 1. 初始化串口 ``` /*定义串口句柄*/ UART_HandleTypeDef huart2; /*串口初始化*/ void MX_USART2_UART_Init(void) { huart2.Instance = USART2; huart2.Init.BaudRate = 9600; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart2) != HAL_OK) { Error_Handler(); } } ``` 2. 定义RS485控制引脚 ``` /*定义RS485控制引脚*/ #define RS485_DIR_GPIO_Port GPIOA #define RS485_DIR_Pin GPIO_PIN_12 /*RS485控制引脚设置为输出*/ HAL_GPIO_WritePin(RS485_DIR_GPIO_Port, RS485_DIR_Pin, GPIO_PIN_RESET); GPIO_InitStruct.Pin = RS485_DIR_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(RS485_DIR_GPIO_Port, &GPIO_InitStruct); ``` 3. RS485发送数据 ``` /*RS485发送数据*/ void RS485_SendData(uint8_t *pData, uint16_t Size) { /*设置为发送模式*/ HAL_GPIO_WritePin(RS485_DIR_GPIO_Port, RS485_DIR_Pin, GPIO_PIN_SET); /*发送数据*/ HAL_UART_Transmit(&huart2, pData, Size, 1000); /*等待发送完成*/ HAL_UART_Transmit(&huart2, NULL, 0, 1000); /*设置为接收模式*/ HAL_GPIO_WritePin(RS485_DIR_GPIO_Port, RS485_DIR_Pin, GPIO_PIN_RESET); } ``` 4. RS485接收数据 ``` /*定义接收缓冲区*/ uint8_t RxBuffer[256]; /*定义接收状态*/ typedef enum { RX_IDLE = 0, RX_BUSY, }RX_STATUS; /*定义接收状态*/ RX_STATUS RxState = RX_IDLE; /*RS485接收数据*/ void RS485_ReceiveData(void) { /*接收数据*/ uint8_t data; if(HAL_UART_Receive(&huart2, &data, 1, 10) == HAL_OK) { /*设置为接收模式*/ HAL_GPIO_WritePin(RS485_DIR_GPIO_Port, RS485_DIR_Pin, GPIO_PIN_RESET); /*保存数据到接收缓冲区*/ RxBuffer[RxIndex++] = data; /*接收数据完成*/ if(RxIndex >= RxSize) { RxState = RX_IDLE; } } } ``` 5. 实现RS485通信协议 根据实际应用需要,可以设计不同的通信协议,在此只提供一种简的实现。 ``` /*定义协议帧结构体*/ typedef struct { uint8_t Addr; /*地址*/ uint8_t Func; /*功能码*/ uint8_t Data[256]; /*数据*/ uint16_t Size; /*数据长度*/ uint16_t Crc; /*校验码*/ }PROTOCOL_FRAME; /*定义地址*/ #define ADDR_MASTER 0x01 #define ADDR_SLAVE 0x02 /*定义功能码*/ #define FUNC_READ 0x03 #define FUNC_WRITE 0x06 /*发送读取数据请求*/ void SendReadRequest(uint8_t Addr, uint16_t RegAddr, uint16_t RegNum) { PROTOCOL_FRAME frame; /*设置协议帧*/ frame.Addr = Addr; frame.Func = FUNC_READ; frame.Data[0] = RegAddr >> 8; frame.Data[1] = RegAddr & 0xff; frame.Data[2] = RegNum >> 8; frame.Data[3] = RegNum & 0xff; frame.Size = 4; /*发送数据*/ RS485_SendData((uint8_t*)&frame, frame.Size+2); } /*解析接收到的数据*/ void ParseRxData(void) { PROTOCOL_FRAME *frame = (PROTOCOL_FRAME*)RxBuffer; /*判断是否为读取数据响应*/ if(frame->Addr == ADDR_SLAVE && frame->Func == FUNC_READ && frame->Size == RxSize-6) { /*校验数据*/ uint16_t crc = CRC16_Modbus(RxBuffer, RxSize-2); if(crc == (frame->Crc>>8)|(frame->Crc<<8)) { /*处理数据*/ uint16_t *pData = (uint16_t*)frame->Data; for(int i=0; i<frame->Size/2; i++) { printf("RegAddr: %d, RegValue: %d\n", i, pData[i]); } } } } ``` 以上是基于HAL库的STM32+RS485通讯协议代码详解,实现了RS485的发送和接收,并设计了简通信协议。由于应用场景不同,具体实现可能会有所不同,需要根据实际情况进行调整和优化。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值