参考教程
第三节 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