STM32单片机实现modbus通讯协议

 0.内容简介

1. modbus通讯协议

(1)modbus通讯协议是由Modicon公司在1979年开发的,应用于工业现场控制的总线协议。

(2)modbus协议采用主从模式,由一个主机对多个从机进行通信,最多支持247个从机。

(3)modbus数据传输模式:ASCII和RTU两种模式。

        1)ASCII模式:

① 有开始标志和结束标志,数据帧以英文冒号(0x3A)开始,以回车和换号(0x0D and 0x0A )符号结束;一个数据帧的各字节的传输时间间隔可以大于1s。

② 允许的传输的字符集为十六进制的0~9和A~F;每个8位的字节被拆分成两个ASCII字符进行发送,比如十六进制数0xAF ,会被分解成ASCII字符“A”和“F”进行发送,发送的字符量比RTU增加一倍。

        2)RTU模式:

① 没有规定的开始和结束标记,规定每两个字节之间发送或者接收的时间间隔不能超过3.5倍字符传输时间。如果两个字符时间间隔超过了3.5倍的字符传输时间,就认为一帧数据已经接收,新的一帧数据传输开始。

② 每个字节可以传输两个十六进制字符,比如十六进制数0xAF,直接以十六进制0xAF进行发送

        3)modbus数据帧格式:ModBus协议帧格式(RTU、ASCII、TCP/IP)_匠在江湖的博客-CSDN博客_modbus协议帧格式

① modbus数据帧校验方式:CRC循环冗余校验(RTU模式)和LRC纵向冗余校验(ASCII模式)

② modbus从机地址:主机通过唯一的从机地址识别从机,主机与从机通信时通过不同的功能码实现不同的功能

③ modbus功能码:MODBUS协议整理——功能码简述 - Endv - 博客园

2. RS485

3. STM32单片机

3.1 STM32F103C8T6单片机

STM32F103 | 产品 | STM32/STM8 | MCU单片机 | 意法半导体STM | 意法半导体STM | STM32/STM8微控制器 | MCU单片机

3.2 串口通信

3.2.0 内容简介

  • 串口通信:波特率:9600    数据位:8   校验位:无 停止位:1
  • 串口调试:

3.2.1 modbusUsart初始化

void modbusUsart_Init(void) {
	USART_InitTypeDef USART_InitStructure;
	GPIO_InitTypeDef  GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  // GPIOA时钟使能
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); // USART2时钟使能
	
	// USART1_TX   A2
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_Init(GPIOA, &GPIO_InitStructure);	 
	// USART1_RX	  A3
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 
	GPIO_Init(GPIOA, &GPIO_InitStructure);  
	// 串口参数设置
	USART_InitStructure.USART_BaudRate = 9600;                                      // 波特率 // 波特率:9600bps,9600bit/s => 1/9600bps = 104us/bit
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;                     // 字长:8
	USART_InitStructure.USART_StopBits = USART_StopBits_1;                          // 停止位:1
	USART_InitStructure.USART_Parity = USART_Parity_No;                             // 奇偶校验位:0
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 硬件数据流控制:无
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                 // 串口模式:收发模式
  // 串口设置
	USART_DeInit(USART2);                        // 串口复位
	USART_Init(USART2, &USART_InitStructure);    // 串口参数初始化
	USART_ITConfig(USART2,USART_IT_RXNE,ENABLE); // 串口中断开启
	USART_Cmd(USART2, ENABLE);                   // 串口使能
	USART_ClearFlag(USART2,USART_FLAG_TC);       // 清除标志位
	
	// NVIC
	NVIC_InitTypeDef  NVIC_InitStructure;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); 
	NVIC_InitStructure.NVIC_IRQChannel=USART2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;	
	NVIC_Init(&NVIC_InitStructure);
}

3.2.2 modbusUsart发送器

void modbusUsart_Transmit(u8 data) {  // modbusUsart发送一个字节
    USART_SendData(USART2, data); // 发送数据
	
	while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); // 串口状态读取            
    USART_ClearFlag(USART2,USART_FLAG_TC);                   // 清除标志位
}

3.3.4 Usart接收结束中断

void USART2_IRQHandler() { // USART2中断,MODBUS字节接收中断
	u8 receiveBuffer;
	receiveBuffer=USART_ReceiveData(USART2); // 读串口数据
	switch(receiveBuffer) {
		case 1: modbusUsart_Transmit(1); break; // PC:TX->01; RX->01
		case 2: modbusUsart_Transmit(2); break; // PC:TX->02; RX->02
		case 3: modbusUsart_Transmit(3); break; // PC:TX->03; RX->03
		default: break;
	}
}

3.3.5 main.c

#include "modbusUsart.h"

int main() {
    modbusUsart_Init();
    while(1) {
		// TODO
	}
}

3.3 定时器

3.3.0 内容简介

  • 串口调试:实现Timer2溢出中断,1ms(实际1.024ms)触发一次,间隔约1s接收一次数据。

3.3.1 Timer2初始化

void Timer2_Init() { // 初始化Timer2
	TIM_TimeBaseInitTypeDef timer;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); // 时钟TIM2使能
	
	TIM_DeInit(TIM2);
	timer.TIM_Period=1000-1;  								// 设置自动重载计数周期值:1000,1us*1000=1ms
	timer.TIM_Prescaler=72-1; 								// 分频系数:72M/72=1MHZ,1us
	timer.TIM_ClockDivision=TIM_CKD_DIV1; 		// 时钟分频因子
	timer.TIM_CounterMode=TIM_CounterMode_Up; // 计数方式:TIM 向上计数
	TIM_TimeBaseInit(TIM2,&timer);            // 初始化TIM2
	TIM_Cmd(TIM2,ENABLE);                     // 使能TIM2
	TIM_ITConfig(TIM2, TIM_IT_Update,ENABLE); // 允许中断更新
	
	// NVIC
	NVIC_InitTypeDef  NVIC_InitStructure;
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); 
	NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;	
	NVIC_Init(&NVIC_InitStructure);
}

3.3.2 Timer溢出中断

void TIM2_IRQHandler() { // Timer2溢出中断
	u8 timFlagStatus;
  timFlagStatus= TIM_GetFlagStatus(TIM2, TIM_FLAG_Update);	
	if(timFlagStatus==SET) {
		TIM_ClearFlag(TIM2, TIM_FLAG_Update);
		timerCounter++;
	}	
}

3.3.3 main.c

#include "timer.h"
#include "modbusUsart.h"

#include "core_cm3.h"
void delay_us(u32 delay_us);    
void delay_ms(u16 delay_ms);

u16 timerCounter=0;

int main() {
    Timer2_Init();      // Timer2初始化 
	modbusUsart_Init(); // modbusUsart初始化
	
  while(1) {
		modbusUsart_Transmit(timerCounter/1000);
		delay_ms(1000);
	}
}

3.3 modbus通讯协议

3.3.0 内容简介

  • modbus调试:实现03、06功能码

3.3.1 modbusCRC校验

#include "modbusCRC.h"

const unsigned char crcTableHigh[] = { // 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
};

const unsigned char crcTableLow[] = {  // 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
};

unsigned int ModbusCRC16(unsigned char *sendBuffer, unsigned int sendCounter) { // ModbusCRC16校验
    unsigned char crcHigh = 0xFF; // CRC高字节初始化
    unsigned char crcLow = 0xFF;  // CRC低字节初始化
    unsigned long crcIndex;       // CRC索引
    while(sendCounter--) {
			crcIndex = crcHigh ^ *sendBuffer++;
			crcHigh = crcLow ^ crcTableHigh[crcIndex];
			crcLow = crcTableLow[crcIndex];
    }
    return (crcHigh<<8|crcLow);
}

3.3.2 modbus功能

#ifndef __MODBUS_H
#define __MODBUS_H

#include "stm32f10x.h"

typedef struct {
	u8 mAddress;           // modbus: 本从设备地址 
	u8 mReceiveBuffer[50]; // modbus: 接受数据缓存 
	u8 mSendBuffer[50];    // modbus: 发送数据缓存
	u16 mTimer;            // modbus: 计时器,记录字符间隔
	u8 mTimerFlag;         // modbus: 计时器标志,0停止计时器,1启动计时器
	u8 mReceiveCounter;    // modbus: 接收数据计数器
	u8 mReceiveFlag;       // modbus: 接收标志,1接收完成,0正在接收
} MODBUS;

extern MODBUS modbus; // modbus引用

void Modbus_Init(void);          // Func1: modbus 初始化
void Modbus_Event(void); 	       // Func2: modbus 事件
void Modbus_FunctionCode3(void); // Func3: modbus功能码03功能实现 
void Modbus_FunctionCode6(void); // Func4: modbus功能码06功能实现

#endif

3.3.3 main.c 

#include "timer.h"
#include "modbusUsart.h"
#include "modbus.h"

int main() {
  Timer2_Init(); // Timer2初始化 
  Modbus_Init(); // modbus初始化
  while(1) {
		Modbus_Event(); // 处理MODbus数据
	}
}

  • 20
    点赞
  • 136
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云水僧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值