【Modbus学习笔记】stm32实现Modbus

参考教程

第三节 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.回调函数

cpp 复制代码
//回调函数,定时器中断服务函数调用
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字节

  1. 03功能码函数
cpp 复制代码
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.完整代码

cpp 复制代码
//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
cpp 复制代码
//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
cpp 复制代码
//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
相关推荐
我爱挣钱我也要早睡!4 小时前
Java 复习笔记
java·开发语言·笔记
知识分享小能手6 小时前
React学习教程,从入门到精通, React 属性(Props)语法知识点与案例详解(14)
前端·javascript·vue.js·学习·react.js·vue·react
汇能感知9 小时前
摄像头模块在运动相机中的特殊应用
经验分享·笔记·科技
阿巴Jun9 小时前
【数学】线性代数知识点总结
笔记·线性代数·矩阵
茯苓gao9 小时前
STM32G4 速度环开环,电流环闭环 IF模式建模
笔记·stm32·单片机·嵌入式硬件·学习
是誰萆微了承諾9 小时前
【golang学习笔记 gin 】1.2 redis 的使用
笔记·学习·golang
DKPT10 小时前
Java内存区域与内存溢出
java·开发语言·jvm·笔记·学习
aaaweiaaaaaa10 小时前
HTML和CSS学习
前端·css·学习·html
ST.J10 小时前
前端笔记2025
前端·javascript·css·vue.js·笔记
Suckerbin10 小时前
LAMPSecurity: CTF5靶场渗透
笔记·安全·web安全·网络安全