【Modbus学习笔记】stm32实现Modbus

发布于:2025-07-05 ⋅ 阅读:(17) ⋅ 点赞:(0)

参考教程  

第三节 STM32实现modbus(上)_哔哩哔哩_bilibili 

1、 STM32实现modbus要解决的问题

01 接收发送数据→串口

02 时间间隔问题→要求9600,推荐19200

03 逻辑代码→C语言

2、 两帧之间大于3.5个字节时间如何解决

定时器

已接受到数据,没接收到后计时大于4说明这一帧完毕

3、 编程注意事项

01            03                 00 00 00 0A C5 CD

发送/接收顺序: 自左到右

校验:0xcdc5(注意)

字节时间的计算:

1 个起始位,8 个数据位,0个校验位,1 个停止位,波特率19200

10bit 19200/10=1920 Byte,1/1920=520us,520*3.5

4、代码讲解

1.回调函数

//回调函数,定时器中断服务函数调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim==(&TIM3_Handler))
    {
        modbus_time++;
			 if(modbus_time >4 && ((USART_RX_STA&0X3FFF) !=0))
			 {
				 USART_RX_STA|=0x8000;
			 }
    }
}

USART_RX_STA&0X3FFF) !=0表示接收到数据
USART_RX_STA|=0x8000;  表示接收完成了

>4 保证两帧之间大于3.5字节

2. 03功能码函数

void modbus_03_function(void)
{
	u16 i;
	u16 arr_start=3;   //从机回复寄存器的值是从数组的第3位开始
	u16 register_len;  //查询寄存器的数量
	u16 register_start;		 //查询寄存器的起始地址
	modbus_Tx_buff[0] = modbus_slave_addr;//从机的地址
	modbus_Tx_buff[1] = 03;//功能码
	register_len = USART_RX_BUF[5]|(((u16)USART_RX_BUF[4])<<8); 
	modbus_Tx_buff[2] = register_len * 2;
	register_start = USART_RX_BUF[3]|(((u16)USART_RX_BUF[2])<<8);
	for(i=0 ; register_len >i ; i++,register_start++)
	{
		modbus_Tx_buff[arr_start]=modbus_virtual_register[register_start]>>8&0xFF;         
		arr_start++;
		modbus_Tx_buff[arr_start]=modbus_virtual_register[register_start]&0xFF;
		arr_start++;
	}
	arr_start=CRC16(modbus_Tx_buff,register_start*2+3);
	modbus_Tx_buff[register_start*2+3]=(arr_start)&0xFF;    //取低位     
	modbus_Tx_buff[register_start*2+4]=(arr_start>>8)&0xFF;//取高位
	modbus_send_data(modbus_Tx_buff,register_start*2+5);//发送
}


 u16 arr_start=3;表示有效位从第三位开始
USART_RX_BUF[5]|(((u16)USART_RX_BUF[4])<<8)  表示高位先发送
crc校验中先低位后高位:
    (arr_start)&0xFF 表示取低位
    (arr_start>>8)&0xFF 表示取高位

5.完整代码 

//modbus.c
#include "modbus.h"
#include "timer.h"
#include "usart.h"

u8	modbus_slave_addr=1;					//从机地址
u8	modbus_Tx_buff[100];		//发送缓冲区
u16 modbus_virtual_register[10]={1,2,3,4,5,6,7,8,9,0}; //虚拟寄存器



/* 发送数据函数,buff为发送内容,len表示发送字节数 */
void modbus_send_data(u8 *buff,u8 len)
{
		HAL_UART_Transmit(&UART1_Handler,(uint8_t*)buff,len,1000);      //发送数据
	  while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET);		//等待发送结束
}


/* modbus服务函数 */
void modbus_service(void)
{
	u16 data_CRC_value;   		//从机收到的CRC校验码
	u16 data_len;         		//从机收到的modbus数据长度
	u16 CRC_check_result; 		//从机根据收到的数据计算出来的CRC校验码
	if(USART_RX_STA&0x8000)   //串口接收完成的标志
	{
		data_len = USART_RX_STA & 0x3fff ; 			
		CRC_check_result = CRC16(USART_RX_BUF,data_len-2);
		data_CRC_value= USART_RX_BUF[data_len-1]<<8 | (((u16)USART_RX_BUF[data_len-2])); //对于CRC的接收,先接收低位,再接收高位
		if(CRC_check_result==data_CRC_value)
		{
			if(USART_RX_BUF[0]==modbus_slave_addr)
			{
				switch(USART_RX_BUF[1])
				{
					case 03:     //读多个寄存器
					{
						modbus_03_function();
						break;
					}
					case 06:      //写单个寄存器
					{
						modbus_06_function();
						break;
					}
					case 16:      //写多个寄存器
					{
						modbus_16_function();
						break;
					}

				}
			}
		}
		USART_RX_STA=0;  //开始下一次接收
	}
}



void modbus_03_function(void)
{
	u16 i;
	u16 arr_start=3;   //从机回复寄存器的值是从数组的第3位开始
	u16 register_len;  //查询寄存器的数量
	u16 register_start;		 //查询寄存器的起始地址
	modbus_Tx_buff[0] = modbus_slave_addr;
	modbus_Tx_buff[1] = 03;
	register_len = USART_RX_BUF[5]|(((u16)USART_RX_BUF[4])<<8); 
	modbus_Tx_buff[2] = register_len * 2;
	register_start = USART_RX_BUF[3]|(((u16)USART_RX_BUF[2])<<8);
	for(i=0 ; register_len >i ; i++,register_start++)
	{
		modbus_Tx_buff[arr_start]=modbus_virtual_register[register_start]>>8&0xFF;         
		arr_start++;
		modbus_Tx_buff[arr_start]=modbus_virtual_register[register_start]&0xFF;
		arr_start++;
	}
	arr_start=CRC16(modbus_Tx_buff,register_start*2+3);
	modbus_Tx_buff[register_start*2+3]=(arr_start)&0xFF;         
	modbus_Tx_buff[register_start*2+4]=(arr_start>>8)&0xFF;
	modbus_send_data(modbus_Tx_buff,register_start*2+5);
}



void modbus_06_function(void)
{
	u16 register_addr;				//主机想要修改从机的指定寄存器编号
	
	modbus_Tx_buff[0]=USART_RX_BUF[0];
	modbus_Tx_buff[1]=USART_RX_BUF[1];
	modbus_Tx_buff[2]=USART_RX_BUF[2];
	modbus_Tx_buff[3]=USART_RX_BUF[3];
	modbus_Tx_buff[4]=USART_RX_BUF[4];
	modbus_Tx_buff[5]=USART_RX_BUF[5];
	
	register_addr = USART_RX_BUF[3]|(((u16)USART_RX_BUF[2])<<8);
	modbus_virtual_register[register_addr] = USART_RX_BUF[5]|(((u16)USART_RX_BUF[4])<<8);
	
	register_addr=CRC16(modbus_Tx_buff,6);
	modbus_Tx_buff[6]=(register_addr)&0xFF;
	modbus_Tx_buff[7]=(register_addr>>8)&0xFF;
	
	modbus_send_data(modbus_Tx_buff,8);
}


void modbus_16_function(void)
{
	u16 i;
	u16 register_start;	   //修改寄存器的起始地址
	u16 register_num;		   //修改寄存器的数量
	u16 CRC_check_result;  //CRC校验的结果
	modbus_Tx_buff[0]=USART_RX_BUF[0];
	modbus_Tx_buff[1]=USART_RX_BUF[1];
	modbus_Tx_buff[2]=USART_RX_BUF[2];
	modbus_Tx_buff[3]=USART_RX_BUF[3];
	modbus_Tx_buff[4]=USART_RX_BUF[4];
	modbus_Tx_buff[5]=USART_RX_BUF[5];
	CRC_check_result=CRC16(modbus_Tx_buff,6);
	modbus_Tx_buff[6]=(CRC_check_result)&0xFF;
	modbus_Tx_buff[7]=(CRC_check_result>>8)&0xFF;
	
	register_start = USART_RX_BUF[3]|(((u16)USART_RX_BUF[2])<<8);  
	register_num = USART_RX_BUF[5]|(((u16)USART_RX_BUF[4])<<8);  
	for( i =0;i<register_num;i++)
	{
		modbus_virtual_register[register_start+i] = USART_RX_BUF[8+i*2]|(((u16)USART_RX_BUF[7+i*2])<<8);
	}
	modbus_send_data(modbus_Tx_buff,8);
	
}

unsigned int CRC16(unsigned char *puchMsg,  unsigned char usDataLen)/* 函数以 unsigned short 类型返回 CRC */
/* puchMsg 用于计算 CRC 的报文 */
/* usDataLen 报文中的字节数 */
{ 

	unsigned char uchCRCHi = 0xFF ; /* CRC 的高字节初始化 */
	unsigned char uchCRCLo = 0xFF ; /* CRC 的低字节初始化 */
	unsigned short int uIndex ; /* CRC 查询表索引 */
  unsigned char auchCRCHi[] = { 
		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
    } ;  
	unsigned char auchCRCLo[] = {  
		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
    } ;
 
   while (usDataLen--) /* 完成整个报文缓冲区 */
	{
		uIndex = uchCRCLo ^ *puchMsg++ ; /* 计算 CRC */
		uchCRCLo = uchCRCHi ^ auchCRCHi[uIndex] ;
		uchCRCHi = auchCRCLo[uIndex] ;
	}
	return (uchCRCHi << 8 | uchCRCLo) ;
}




//modbus.h
#ifndef __modbus_H
#define __modbus_H
#include "stdio.h"	
#include "sys.h" 

void modbus_service(void);
void modbus_03_function(void);
void modbus_06_function(void);
void modbus_16_function(void);
void modbus_send_data(u8 *buff,u8 len);


unsigned int CRC16(unsigned char *puchMsg,  unsigned char usDataLen);

#endif

//timer.c
#include "timer.h"
#include "led.h"
#include "usart.h"
//////////////////////////////////////////////////////////////////////////////////	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F103开发板
//定时器中断驱动代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2019/9/17
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved									  
////////////////////////////////////////////////////////////////////////////////// 	
unsigned int modbus_time;
TIM_HandleTypeDef TIM3_Handler;      //定时器句柄 

//通用定时器3中断初始化
//arr:自动重装值。
//psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位:Mhz
//这里使用的是定时器3!
void TIM3_Init(u16 arr,u16 psc)
{  
    TIM3_Handler.Instance=TIM3;                          //通用定时器3
    TIM3_Handler.Init.Prescaler=psc;                     //分频系数
    TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;    //向上计数器
    TIM3_Handler.Init.Period=arr;                        //自动装载值
    TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//时钟分频因子
    HAL_TIM_Base_Init(&TIM3_Handler);
    
    HAL_TIM_Base_Start_IT(&TIM3_Handler); //使能定时器3和定时器3更新中断:TIM_IT_UPDATE   
}

//定时器底册驱动,开启时钟,设置中断优先级
//此函数会被HAL_TIM_Base_Init()函数调用
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance==TIM3)
	{
		__HAL_RCC_TIM3_CLK_ENABLE();            //使能TIM3时钟
		HAL_NVIC_SetPriority(TIM3_IRQn,1,3);    //设置中断优先级,抢占优先级1,子优先级3
		HAL_NVIC_EnableIRQ(TIM3_IRQn);          //开启ITM3中断   
	}
}

//定时器3中断服务函数
void TIM3_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&TIM3_Handler);
}

//回调函数,定时器中断服务函数调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim==(&TIM3_Handler))
    {
        modbus_time++;
			 if(modbus_time >4 && ((USART_RX_STA&0X3FFF) !=0))
			 {
				 USART_RX_STA|=0x8000;
			 }
    }
}
//timer.h
#ifndef _TIMER_H
#define _TIMER_H
#include "sys.h"
//////////////////////////////////////////////////////////////////////////////////	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F103开发板
//定时器驱动代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2019/9/17
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved									  
////////////////////////////////////////////////////////////////////////////////// 	
extern TIM_HandleTypeDef TIM3_Handler;      //定时器句柄 
extern unsigned int modbus_time;
void TIM3_Init(u16 arr,u16 psc);
#endif
//usart.c
#include "sys.h"
#include "usart.h"	
#include "timer.h"
////////////////////////////////////////////////////////////////////////////////// 	 
//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "includes.h"					//ucos 使用	  
#endif
//////////////////////////////////////////////////////////////////////////////////	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//串口1初始化		   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2019/9/17
//版本:V1.5
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved
//********************************************************************************
//V1.3修改说明 
//支持适应不同频率下的串口波特率设置.
//加入了对printf的支持
//增加了串口接收命令功能.
//修正了printf第一个字符丢失的bug
//V1.4修改说明
//1,修改串口初始化IO的bug
//2,修改了USART_RX_STA,使得串口最大接收字节数为2的14次方
//3,增加了USART_REC_LEN,用于定义串口最大允许接收的字节数(不大于2的14次方)
//4,修改了EN_USART1_RX的使能方式
//V1.5修改说明
//1,增加了对UCOSII的支持
////////////////////////////////////////////////////////////////////////////////// 	  
 

//////////////////////////////////////////////////////////////////
//加入以下代码,支持printf函数,而不需要选择use MicroLIB	  
#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{ 
	int handle; 

}; 

FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
void _sys_exit(int x) 
{ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{      
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
    USART1->DR = (u8) ch;      
	return ch;
}
#endif 


#if EN_USART1_RX   //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误   	
u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
u16 USART_RX_STA=0;       //接收状态标记	  

u8 aRxBuffer[RXBUFFERSIZE];//HAL库使用的串口接收缓冲
UART_HandleTypeDef UART1_Handler; //UART句柄
  
//初始化IO 串口1 
//bound:波特率
void uart_init(u32 bound)
{	
	//UART 初始化设置
	UART1_Handler.Instance=USART1;					    //USART1
	UART1_Handler.Init.BaudRate=bound;				    //波特率
	UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B;   //字长为8位数据格式
	UART1_Handler.Init.StopBits=UART_STOPBITS_1;	    //一个停止位
	UART1_Handler.Init.Parity=UART_PARITY_NONE;		    //无奇偶校验位
	UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE;   //无硬件流控
	UART1_Handler.Init.Mode=UART_MODE_TX_RX;		    //收发模式
	HAL_UART_Init(&UART1_Handler);					    //HAL_UART_Init()会使能UART1
	
	HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE);//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
  
}

//UART底层初始化,时钟使能,引脚配置,中断配置
//此函数会被HAL_UART_Init()调用
//huart:串口句柄

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    //GPIO端口设置
	GPIO_InitTypeDef GPIO_Initure;
	
	if(huart->Instance==USART1)//如果是串口1,进行串口1 MSP初始化
	{
		__HAL_RCC_GPIOA_CLK_ENABLE();			//使能GPIOA时钟
		__HAL_RCC_USART1_CLK_ENABLE();			//使能USART1时钟
		__HAL_RCC_AFIO_CLK_ENABLE();
	
		GPIO_Initure.Pin=GPIO_PIN_9;			//PA9
		GPIO_Initure.Mode=GPIO_MODE_AF_PP;		//复用推挽输出
		GPIO_Initure.Pull=GPIO_PULLUP;			//上拉
		GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;//高速
		HAL_GPIO_Init(GPIOA,&GPIO_Initure);	   	//初始化PA9

		GPIO_Initure.Pin=GPIO_PIN_10;			//PA10
		GPIO_Initure.Mode=GPIO_MODE_AF_INPUT;	//模式要设置为复用输入模式!	
		HAL_GPIO_Init(GPIOA,&GPIO_Initure);	   	//初始化PA10
		
#if EN_USART1_RX
		HAL_NVIC_EnableIRQ(USART1_IRQn);				//使能USART1中断通道
		HAL_NVIC_SetPriority(USART1_IRQn,3,3);			//抢占优先级3,子优先级3
#endif	
	}
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance==USART1)//如果是串口1
	{
		if((USART_RX_STA&0x8000)==0)//接收未完成
		{
//			if(USART_RX_STA&0x4000)//接收到了0x0d
//			{
//				if(aRxBuffer[0]!=0x0a)USART_RX_STA=0;//接收错误,重新开始
//				else USART_RX_STA|=0x8000;	//接收完成了 
//			}
//			else //还没收到0X0D
//			{	
//				if(aRxBuffer[0]==0x0d)USART_RX_STA|=0x4000;
//				else
//				{
					modbus_time = 0;
					USART_RX_BUF[USART_RX_STA&0X3FFF]=aRxBuffer[0] ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
//				}		 
//			}
		}

	}
}
 
//串口1中断服务程序
void USART1_IRQHandler(void)                	
{ 
	u32 timeout=0;
#if SYSTEM_SUPPORT_OS	 	//使用OS
	OSIntEnter();    
#endif
	
	HAL_UART_IRQHandler(&UART1_Handler);	//调用HAL库中断处理公用函数
	
	timeout=0;
    while (HAL_UART_GetState(&UART1_Handler) != HAL_UART_STATE_READY)//等待就绪
	{
	 timeout++;////超时处理
     if(timeout>HAL_MAX_DELAY) break;		
	
	}
     
	timeout=0;
	while(HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)//一次处理完成之后,重新开启中断并设置RxXferCount为1
	{
	 timeout++; //超时处理
	 if(timeout>HAL_MAX_DELAY) break;	
	}
#if SYSTEM_SUPPORT_OS	 	//使用OS
	OSIntExit();  											 
#endif
} 
#endif	

/*下面代码我们直接把中断控制逻辑写在中断服务函数内部。*/

//串口1中断服务程序
//void USART1_IRQHandler(void)                	
//{ 
//	u8 Res;
//	HAL_StatusTypeDef err;
//#if SYSTEM_SUPPORT_OS	 	//使用OS
//	OSIntEnter();    
//#endif
//	if((__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_RXNE)!=RESET))  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
//	{
//		Res=USART1->DR; 
//		if((USART_RX_STA&0x8000)==0)//接收未完成
//		{
//			if(USART_RX_STA&0x4000)//接收到了0x0d
//			{
//				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
//				else USART_RX_STA|=0x8000;	//接收完成了 
//			}
//			else //还没收到0X0D
//			{	
//				if(Res==0x0d)USART_RX_STA|=0x4000;
//				else
//				{
//					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
//					USART_RX_STA++;
//					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
//				}		 
//			}
//		}   		 
//	}
//	HAL_UART_IRQHandler(&UART1_Handler);	
//#if SYSTEM_SUPPORT_OS	 	//使用OS
//	OSIntExit();  											 
//#endif
//} 
//#endif	

//usart.h
#ifndef __USART_H
#define __USART_H
#include "stdio.h"	
#include "sys.h" 
//////////////////////////////////////////////////////////////////////////////////	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//串口1初始化		   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2019/9/17
//版本:V1.5
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved
//********************************************************************************
//V1.3修改说明 
//支持适应不同频率下的串口波特率设置.
//加入了对printf的支持
//增加了串口接收命令功能.
//修正了printf第一个字符丢失的bug
//V1.4修改说明
//1,修改串口初始化IO的bug
//2,修改了USART_RX_STA,使得串口最大接收字节数为2的14次方
//3,增加了USART_REC_LEN,用于定义串口最大允许接收的字节数(不大于2的14次方)
//4,修改了EN_USART1_RX的使能方式
//V1.5修改说明
//1,增加了对UCOSII的支持
#define USART_REC_LEN  			200  		//定义最大接收字节数 200
#define EN_USART1_RX 			1			//使能(1)/禁止(0)串口1接收
	  	
extern u8  USART_RX_BUF[USART_REC_LEN]; 	//接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
extern u16 USART_RX_STA;         			//接收状态标记	
extern UART_HandleTypeDef UART1_Handler; 	//UART句柄

#define RXBUFFERSIZE   1 					//缓存大小
extern u8 aRxBuffer[RXBUFFERSIZE];			//HAL库USART接收Buffer

//如果想串口中断接收,请不要注释以下宏定义
void uart_init(u32 bound);
#endif