基于STM32F103C8T6的RS485 MODBUS通信及CRC校验

     在进行RS485 MODBUS收发通信,无固定帧头及帧尾的数据包传输时,两组数据包之间需要有3.5个字符的时间间隔,通过定时器中断延时判定数据包是否传输完成,同时进行CRC校验比对

一、串口通信配置

与常规USART配置基本一致,只是多一个控制发送与接收切换的引脚,下例为USART2的配置,使用GPIOA_PIN_1为控制引脚

Rs485.h

#ifndef __RS485_H
#define __RS485_H

#include <stdio.h>

extern uint8_t  Rs485_TxPacket[];
extern uint8_t  Rs485_RxPacket[];
extern uint8_t  Rs485_pRxPacket;	 
extern uint8_t  Rs485_RxStar;
extern uint8_t  Rs485_RxDataLen; 
extern uint32_t Rs485_RxDlay;
extern uint8_t  Rs485_RxFlag;

void Rs485_Init(void);
void Rs485_SendByte(uint8_t Byte);
void Rs485_SendArray(uint8_t *Array, uint16_t Length);
void Rs485_SendString(char *String);
void Rs485_SendNumber(uint32_t Number, uint8_t Length);
uint8_t Rs485_GetRxFlag(void);

#endif

Rs485.c

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

uint8_t  Rs485_TxPacket[32] = {0x01,0x02,0x03,0x04};  //定义发送数据包数组
uint8_t  Rs485_RxPacket[32];				          //定义接收数据包数组
uint8_t  Rs485_RxFlag;					              //定义接收数据包完成标志位
uint8_t  Rs485_pRxPacket = 0;	                      //定义接收数据包位置变量
uint8_t  Rs485_RxStar;                                //定义接收数据包开始
uint8_t  Rs485_RxDataLen = 0;                         //定义接收数据包长度
uint32_t Rs485_RxDlay = 0;                            //定义RS485数据包接收完成判定延时变量


/**
  * 函    数:485串口初始化
  * 参    数:无
  * 返 回 值:无
  */
void Rs485_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);	//开启USART2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;              //控制数据的接收、发送
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);		//将PA2引脚初始化为复用推挽输出(USART2  TX)
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);		//将PA3引脚初始化为上拉输入(USART2  RX)
	
	/*USART2初始化*/
	USART_InitTypeDef USART2_InitStructure;					                            //定义结构体变量
	USART2_InitStructure.USART_BaudRate = 9600;				                            //波特率9600
	USART2_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//硬件流控制,不需要
	USART2_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;	                //模式,发送模式和接收模式均选择
	USART2_InitStructure.USART_Parity = USART_Parity_No;		                        //奇偶校验,不需要
	USART2_InitStructure.USART_StopBits = USART_StopBits_1;                          	//停止位,选择1位
	USART2_InitStructure.USART_WordLength = USART_WordLength_8b;		                //字长,选择8位
	USART_Init(USART2, &USART2_InitStructure);				
	
	/*中断输出配置*/
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);		       //开启串口接收数据的中断
	
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);			   //配置NVIC为分组2
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;					   //定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;		   //选择配置NVIC的USART1线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			   //指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //指定抢占优先级为2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		   //指定响应优先级为1
	NVIC_Init(&NVIC_InitStructure);							   //配置NVIC外设
	
	/*USART2使能*/
	USART_Cmd(USART2, ENABLE);								   //使能USART2,串口开始运行
	
	/*USART2默认接收状态*/
	GPIO_ResetBits(GPIOA, GPIO_Pin_1);	
	
}

/**
  * 函    数:Rs485串口发送一个字节
  * 参    数:Byte 要发送的一个字节
  * 返 回 值:无
  */
void Rs485_SendByte(uint8_t Byte)
{
	GPIO_SetBits(GPIOA, GPIO_Pin_1);	                          //发送模式
	USART_SendData(USART2, Byte);		                          //字节数据写入数据寄存器
	while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET); //等待发送完成
	GPIO_ResetBits(GPIOA, GPIO_Pin_1);	                          //接收模式
}

/**
  * 函    数:Rs485串口发送一个数组
  * 参    数:Array 要发送数组的首地址
  * 参    数:Length 要发送数组的长度
  * 返 回 值:无
  */
void Rs485_SendArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)		//遍历数组
	{
		Rs485_SendByte(Array[i]);		//依次调用Rs485_SendByte发送每个字节数据
	}
}

/**
  * 函    数:Rs485串口发送一个字符串
  * 参    数:String 要发送字符串的首地址
  * 返 回 值:无
  */
void Rs485_SendString(char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)//遍历字符数组(字符串),遇到字符串结束标志位后停止
	{
		Rs485_SendByte(String[i]);		//依次调用Rs485_SendByte发送每个字节数据
	}
}

/**
  * 函    数:次方函数(内部使用)
  * 返 回 值:返回值等于X的Y次方
  */
uint32_t Rs485_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;	//设置结果初值为1
	while (Y --)			//执行Y次
	{
		Result *= X;		//将X累乘到结果
	}
	return Result;
}

/**
  * 函    数:Rs485串口发送数字
  * 参    数:Number 要发送的数字,范围:0~4294967295
  * 参    数:Length 要发送数字的长度,范围:0~10
  * 返 回 值:无
  */
void Rs485_SendNumber(uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i ++)		//根据数字长度遍历数字的每一位
	{
        //依次调用Rs485_SendByte发送每位数字
		Rs485_SendByte(Number / Rs485_Pow(10, Length - i - 1) % 10 + '0');	
	}
}

/**
  * 函    数:获取Rs485接收数据包标志位
  * 参    数:无
  * 返 回 值:Rs485接收数据包标志位,范围:0~1,接收数据包后,标志位置1,读取后标志位自动清零
  */
uint8_t Rs485_GetRxFlag(void)
{
	if (Rs485_RxFlag == 1)			//如果标志位为1
	{
		Rs485_RxFlag = 0;
		return 1;					//则返回1,并自动清零标志位
	}
	return 0;						//如果标志位为0,则返回0
}

/**
  * 函    数:USART2中断函数
  * 参    数:无
  * 返 回 值:无
  */
void USART2_IRQHandler(void)
{
	//判断是否是USART2的接收事件触发的中断
    if (USART_GetITStatus(USART2, USART_IT_RXNE) == SET)	       
	{
	   /**使用Time2超时判断数据包接收是否完成
         *RS485接收开始判断 0:未在接收中 1:接收中
         *读取数据寄存器,存放在RS485接收的数据变量	
         *将数据存入RS485数据包数组的指定位置
         *RS485接收数据包的位置自增
         *RS485接收数据包长度自增
         *RS485数据包接收完成判定延时置0
         *清除标志位
         */
		Rs485_RxStar = 1;                                          
		uint8_t Rs485RxData = USART_ReceiveData(USART2);	       
		Rs485_RxPacket[Rs485_pRxPacket] = Rs485RxData;	           
		Rs485_pRxPacket ++;				                           
        Rs485_RxDataLen ++;                                        
		Rs485_RxDlay = 0;                                          
		USART_ClearITPendingBit(USART2, USART_IT_RXNE);		      
	}
}

二、定时器配置

使用TIME2作为定时中断,延时判定数据包接收完成,定时中断函数写在main函数中,1ms定时配置如下:

Timer.h

#ifndef __TIMER_H
#define __TIMER_H

void Timer_Init(void);

#endif

Timer.c

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:定时中断初始化
  * 参    数:无
  * 返 回 值:无
  */
void Timer_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟
	
	/*配置时钟源*/
	TIM_InternalClockConfig(TIM2);	                     //选择TIM2为内部时钟
	
	/**时基单元初始化
	  *定时时间=(频率的倒数*(TIM_Period+1))
	  *例:TIM_Prescaler=720-1,频率=72M/(TIM_Prescaler+1)=100K
	  *例:TIM_Period=100-1,定时时间=(1/频率)*(TIM_Period+1)=0.001s
	  *1s定时可设置TIM_Period=10000-1;TIM_Prescaler=7200-1
      */

    /**定义结构体变量
      *时钟分频,(选择不分频,频率=72M)
      *计数器模式,选择向上计数
      *计数周期,即ARR的值(计100个数)
      *预分频器,即PSC的值(频率=72M/TIM_Prescaler)
      *重复计数器,高级定时器才会用到
      *将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
      */	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	
	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;				    
	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;				
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);				
	
	/*中断输出配置*/
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);	         //清除定时器更新标志位
	
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);	     //开启TIM2的更新中断
	
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	 //配置NVIC为分组2
	
	/*NVIC配置*/
    /**定义结构体变量
      *选择配置NVIC的TIM2线
      *指定NVIC线路使能
      *指定NVIC线路的抢占优先级为3
      *指定NVIC线路的响应优先级为1
      *将结构体变量交给NVIC_Init,配置NVIC外设
      */
	NVIC_InitTypeDef NVIC_InitStructure;						
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;	
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			
	NVIC_Init(&NVIC_InitStructure);								
	
	/*TIM使能*/
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

三、CRC校验码

以CRC-16 / ModBus的模型计算,参考下面列表法及计算法代码:

CRC.H

#ifndef _CRC_H
#define _CRC_H
#define CRC_TABLE 
#ifdef CRC_TABLE
static unsigned char TabH[] = {  //CRC高位字节值表
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
        0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
        0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  
        0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  
        0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,  
        0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  
        0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,  
        0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
        0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
        0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
        0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,  
        0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,  
        0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40  
    } ;  
static unsigned char TabL[] = {  //CRC低位字节值表
        0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,  
        0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,  
        0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,  
        0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,  
        0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,  
        0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,  
        0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,  
        0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,  
        0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,  
        0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,  
        0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,  
        0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,  
        0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,  
        0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,  
        0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,  
        0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,  
        0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,  
        0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,  
        0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,  
        0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,  
        0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,  
        0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,  
        0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,  
        0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,  
        0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,  
        0x43, 0x83, 0x41, 0x81, 0x80, 0x40  
    } ;
#endif
 
uint16_t ModbusCRCCalc(uint8_t *pbuf,uint8_t buflen);
 
#endif

CRC.C

#include "stm32f10x.h"                  // Device header
#include "crc.h"

uint16_t ModbusCRCCalc(uint8_t *pbuf,uint8_t buflen)
{
	
#ifdef CRC_TABLE
	
    int index;
    uint8_t crch = 0xFF;  //高CRC字节
    uint8_t crcl = 0xFF;  //低CRC字节
    
    while (buflen--)  //计算指定长度的CRC
    {
        index = crch ^ *pbuf++;
        crch = crcl ^ TabH[ index];
        crcl = TabL[ index];
    }
   
    return ((crch<<8) | crcl);  
  
#else
 
    uint16_t crc = 0xFFFF;
	uint8_t i,j;
    for (i = 0; i < buflen; i++)
    {
        crc ^= pbuf[i];                  //XOR byte into least sig. byte of crc
        for (j = 0; j < 8; j++)          //Loop over each bit
        {
            if ((crc & 0x0001) != 0)     //If the LSB is set
            {
                crc >>= 1;               //Shift right and XOR 0xA001
                crc ^= 0xA001;
            }
            else                         //Else LSB is not set
            {
                crc >>= 1;               //Just shift right
            }
        }
    }
 
    //高低字节转换
    crc = ((crc & 0x00ff) << 8) | ((crc & 0xff00) >> 8);
    return crc;
	
#endif 
 
}

四、main函数

1、通过Time2的定时中断函数每5S发送待发数据包

(Rs485_TxPacket[32] = {0x01,0x02,0x03,0x04})

2、接收数据包后做CRC校验判定

判定正确后发送“OK”及接收到的数据包

判定错误后发送“ERR”及包含正确CRC校验码的数据包

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
#include "RS485.h"
#include "CRC.h"

/*变量名定义*/

static int8_t Rs485_TxFlag = 1;         //定义RS485发送标志位,上电置一次1,发送一次


int main(void)
{
	/*模块初始化*/
	OLED_Init();		//OLED初始化
	Timer_Init();       //定时器初始化
	Rs485_Init();       //RS485初始化
	
	while (1)
	{			
		/*每5S定时发送待发数据包(Rs485_TxPacket[32] = {0x01,0x02,0x03,0x04})*/
		if (Rs485_TxFlag == 1)          //检查RS485发送标志位
		{
           /**
             *RS485发送标志位置0
             *计算Rs485待发送数据包的CRC值(CRC16_MODBUS算法)
			 *将CRC分高低8位分别传入待发送数据包
             *发送带CRC校验码的Rs485_TxPacket数据
             */
			Rs485_TxFlag = 0;
			uint16_t R485_TxCRC = ModbusCRCCalc(Rs485_TxPacket,4); 
			Rs485_TxPacket[4] = R485_TxCRC >> 8;                   
			Rs485_TxPacket[5] = R485_TxCRC & 0x00FF;   
			Rs485_SendArray(Rs485_TxPacket,6);                    
		}	
		
		/*接收数据包后进行判定处理*/
		if (Rs485_GetRxFlag() == 1)     //获取Rs485接收数据包标志位
		{
			/**
              *计算Rs485接收到的数据包的CRC值(CRC16_MODBUS算法)
			  *计算Rs485接收到的CRC校验码
              */
			uint16_t R485_RxCRCCalc = ModbusCRCCalc(Rs485_RxPacket,Rs485_RxDataLen-2);
			uint16_t R485_RxCRC = (Rs485_RxPacket[Rs485_RxDataLen-2] << 8) | Rs485_RxPacket[Rs485_RxDataLen-1];
			
			/*CRC校验判定处理*/
			if (R485_RxCRCCalc == R485_RxCRC)     //CRC校验正确
			{	
				/**
                  *发送字符"OK"
				  *发送接收到的Rs485_RxPacket数据包
                  */
				Rs485_SendString("OK"); 
				Rs485_SendArray(Rs485_RxPacket,Rs485_RxDataLen);                
			}	

			else                                  //CRC校验错误
			{
				/**
                  *发送字符"ERR"
				  *将正确的CRC分高低8位分别传入数组
				  *将接收到的Rs485_RxPacket数据包带上正确CRC校验码发送回去
                  */
				Rs485_SendString("ERR");  
				Rs485_RxPacket[Rs485_RxDataLen-2] = R485_RxCRCCalc >> 8;        
				Rs485_RxPacket[Rs485_RxDataLen-1] = R485_RxCRCCalc & 0x00FF;  
				Rs485_SendArray(Rs485_RxPacket,Rs485_RxDataLen);                
			}
	
			Rs485_RxDataLen = 0;                 //RS485接收数据包长度置0
		}	
	}
}	

/* 1ms定时器中断函数*/	
	void TIM2_IRQHandler(void)
	{
		static int32_t Rs485_TxDlay = 0;                  //定义RS485发送延时静态变量
		if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)  //判断TIM2中断
		{ 
			if (Rs485_RxStar == 0)                        //RS485接收开始判断 0:未在接收中
			{	
				Rs485_TxDlay ++; 
				if(Rs485_TxDlay > 5000)                   //5S延时(每5S发送一次)
				{
					Rs485_TxDlay = 0;
					Rs485_TxFlag = 1;                     //RS485发送标志位
				}
			}	
			if (Rs485_RxStar == 1)                        //RS485接收开始判断 1:接收中
			{
				Rs485_TxDlay = 0;
				Rs485_RxDlay ++;                          //RS485数据包判定接收完成延时自增
				if (Rs485_RxDlay > 10)                    //10ms延时判定数据包接收完成
				{
					Rs485_RxDlay = 0;                     //RS485数据包接收完成判定延时置0
					Rs485_RxFlag = 1;                     //RS485数据包接收完成标志
					Rs485_RxStar = 0;                     //RS485接收判定置0
					Rs485_pRxPacket = 0;                  //RS485接收数据包位置置0
				}	
			}	
			TIM_ClearITPendingBit(TIM2, TIM_IT_Update);       //清除标志位
		}
	}

RS485是一种串行通信标准,用于在工业自动化领域实现长距离的数据传输。它是一种差分信号协议,使用两条信号线(A线和B线)来传输数据,并通过电平的正负差异来表示数据位的值。STM32F103C8T6是一款基于ARM Cortex-M3内核的单片机芯片,支持RS485通信协议。 要在STM32F103C8T6上实现RS485通信,首先需要进行一些配置。引用中给出了一些初始化代码,包括打开相关时钟和初始化GPIO的配置。具体的代码可以参考引用中的内容。 配置RS485通信需要注意的是,要使用GPIO的复用功能来配置对应的IO口。引用中的代码片段通过调用`GPIO_PinRemapConfig`函数来开启USART1的复用功能。 另外,还需要配置相应的GPIO引脚。引用中给出了配置GPIO引脚的示例代码。根据具体的需求,可以使用`GPIO_InitStructure`结构体来配置GPIO的引脚号、速度和模式。 在进行RS485通信时,需要注意电平标准的兼容性。引用中提到,当电平标准不一致时,可能需要使用电平转换芯片来实现适配。需要根据具体的情况来选择合适的电平转换芯片。 综上所述,要在STM32F103C8T6上实现RS485通信,需要进行一系列的配置,包括时钟配置、GPIO配置、复用功能配置等。具体的配置步骤可以参考引用和引用中的代码示例。同时,要注意电平标准的兼容性,如有需要,可以使用电平转换芯片来实现适配。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [STM32F103C8T6_UART1(RS485通讯)复用PB6+PB7](https://blog.csdn.net/u012415132/article/details/127848799)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [STM32F103C8T6的学习(6)——串口的学习](https://blog.csdn.net/ashun1234/article/details/128907475)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值