IWDG+状态机架构+Modbus通信,在蓝牙夹具项目中的实际应用
引入独立看门狗和有限状态机架构结合Modbus通信,共同构成了一个可靠、健壮的嵌入式系统架构,能够有效处理通信错误、系统异常,并自动恢复正常运行
目录
蓝牙夹具项目大致介绍:
主要任务就是Modbus通信和舵机的控制(请原谅不能做更具体的介绍)。
问题引入:
蓝牙夹具运行的流程是一路串行执行的,由于外界环境或者其本身不可预知的干扰,使得串行执行的程序卡在流程中的某一步,运行时间久一点,将可能导致程序崩溃、胡乱执行等,这对于工业控制来说是大忌!
那应该怎么解决呢?怎么使得串行执行的程序具有更好的可靠性和鲁棒性呢?具体方法请看下文:
原有程序中加入独立看门狗、有限状态机架构,这些机制共同构成了一个可靠、健壮的嵌入式系统架构,能够有效处理通信错误、系统异常,并自动恢复正常运行。(文末附有完整的main.c和modbus.c程序,足以帮助您理解整个项目)
1.独立看门狗(IWDG)的详细工作原理与实现
1.1 IWDG的基本原理
独立看门狗(IWDG)是STM32微控制器中的一个硬件模块,它有自己独立的时钟源(LSI,内部低速时钟,约为40KHz),不依赖于系统主时钟。这使得即使主时钟出现故障,IWDG仍能正常工作,提供可靠的系统监控。
1.2 IWDG在项目中的实现与工作原理
1.2.1 IWDG初始化
在项目中,IWDG的初始化通过以下代码实现:
// 初始化独立看门狗,2秒超时
IWDG_Init(4, 1250); // 2秒的超时时间
这行代码调用了`IWDG_Init`函数,该函数的实现如下:
void IWDG_Init(u8 IWDG_PR, u16 IWDG_RLR)
{
// 使能对IWDG_PR和IWDG_RLR的写操作
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
// 设置IWDG预分频值
IWDG_SetPrescaler(IWDG_PR);
// 设置IWDG重装载值
IWDG_SetReload(IWDG_RLR);
// 重装载IWDG计数器
IWDG_ReloadCounter();
// 使能IWDG
IWDG_Enable();
}
参数解析:
- `IWDG_PR = 4`: 设置预分频系数为4,对应分频值为64(4*2^4 = 64)
- `IWDG_RLR = 1250`: 设置重装载值为1250
超时时间计算:
- IWDG时钟源为LSI,频率约为40KHz
- 超时时间(ms) = IWDG_RLR * (1 << IWDG_PR) / 40
- 超时时间 = 1250 * 64 / 40 = 2000ms = 2s
这意味着如果系统在2秒内没有"喂狗",IWDG计数器将减到0,触发系统复位。
1.2.2 IWDG工作流程
(1)初始化阶段:
- 使能IWDG寄存器写访问
- 设置预分频值(决定IWDG时钟频率)
- 设置重装载值(决定超时时间)
- 首次重装载计数器
- 使能IWDG开始工作
(2)运行阶段:
- IWDG计数器从设定的重装载值开始递减
- 在主循环中定期调用`IWDG_Feed()`函数重装载计数器
- 如果系统正常运行,计数器不会减到0
- 如果系统异常(如死循环、程序跑飞),无法及时喂狗,计数器减到0后触发系统复位
(3)复位触发:
- 当IWDG计数器减到0时,硬件自动触发系统复位
- 系统复位后,程序从头开始执行,重新初始化IWDG
1.2.3项目中的具体应用
在项目的主循环中,IWDG的应用如下:
while(1)
{
// 喂狗
IWDG_Feed();
// 根据当前系统状态执行不同的操作
switch(g_system_state)
{
case SYS_STATE_INIT:
// 初始化过程
g_system_state = SYS_STATE_IDLE;
break;
// ...其他状态处理...
}
// 延时
delay_ms(10);
}
这里有几个关键点:
1.喂狗位置:在主循环的开始处调用`IWDG_Feed()`,确保每次循环都会重置IWDG计数器。
2.状态机与IWDG的配合:
- 如果状态机卡在某个状态(如`SYS_STATE_PROCESSING`)太久,超过2秒没有返回主循环,就无法及时喂狗
- 这种情况下,IWDG会触发系统复位,使系统从异常状态恢复
3.超时检测机制:
- 项目中还实现了软件超时检测(`g_timeout_counter`)
- 但IWDG提供了硬件级别的保护,即使软件超时检测失效,IWDG仍能保证系统复位
1.2.4 IWDG的特殊优势
1.独立时钟源:
- IWDG使用独立的LSI时钟(约40KHz),不依赖系统主时钟
- 即使主时钟出现故障,IWDG仍能正常工作
2.低功耗模式下仍有效:
- IWDG在STOP和STANDBY低功耗模式下仍然有效
- 可以用于从低功耗模式唤醒系统
3.硬件级保护:
- 一旦启动,IWDG不能被软件关闭
- 提供了对软件失控的最后一道防线
1.4 项目中IWDG的实际应用场景
在这个蓝牙夹具项目中,IWDG主要用于以下场景:
1.防止通信死锁:
- 如果Modbus通信过程中出现异常,可能导致系统卡在等待响应的状态
- IWDG确保即使通信模块失效,系统也能在2秒后复位并恢复
2.防止状态机卡死:
- 状态机可能因为各种原因(如条件判断错误)卡在某个状态
- IWDG提供了状态机的"逃生出口",确保系统不会永久卡住
3.防止舵机控制异常:
- 舵机控制过程中可能出现异常(如舵机不响应)
- IWDG确保即使舵机控制模块失效,系统也能复位并尝试重新控制
4.提高系统可靠性:
- 在工业环境中,电磁干扰、电源波动等可能导致程序跑飞
- IWDG能够在这些情况下自动复位系统,提高整体可靠性
1.5 总结
在这个蓝牙夹具项目中,独立看门狗(IWDG)通过以下方式工作:
1.初始化:设置2秒的超时时间(预分频值4,重装载值1250)
2.运行监控:在主循环中定期喂狗,确保系统正常运行
3.异常处理:如果系统异常导致无法及时喂狗,IWDG会在2秒后触发系统复位
4.与状态机结合:作为状态机的安全保障,防止系统卡在某个状态
IWDG是嵌入式系统可靠性设计的重要组成部分,它提供了一种简单而有效的方式来监控系统运行状态,并在系统异常时自动恢复,特别适合需要高可靠性的工业控制应用,如这个蓝牙夹具项目。
2. IWDG触发系统复位的效果与过程
2.1 IWDG触发系统复位的具体过程
2.1.1 计数器减至零:
- 当系统无法及时"喂狗"(调用`IWDG_Feed()`函数)时,IWDG的计数器会从设定值(本项目中为1250)不断减小
- 当计数器减至0时,IWDG硬件电路会自动触发系统复位信号
2.1.2 硬件复位信号生成:
- 这个复位信号与手动按下STM32开发板上的复位按钮效果相同
- 它是一个硬件级别的复位,不依赖于软件的正常运行
2.1.3 MCU复位过程:
- 所有外设寄存器恢复默认值
- 程序计数器(PC)重置为启动地址(通常是0x08000000)
- CPU核心寄存器重置
- 堆栈指针(SP)重新初始化
2.1.4 程序重新启动:
- 系统从头开始执行启动代码
- 重新初始化所有外设
- 重新进入main()函数
2.2 复位后的系统状态
2.2.1 硬件初始化:
- 所有GPIO引脚回到默认状态
- 时钟系统重新配置
- 外设(UART、SPI等)回到未初始化状态
2.2.2 软件初始化:
- 项目中,main()函数会重新执行以下初始化:
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断优先级分组
delay_init(168); // 延时初始化
Usart_Init(); // 串口初始化
uart4_init(9600); // UART4初始化
LED_Init(); // LED初始化
TIM3_Int_Init(10000-1,8400-1); // 定时器初始化
TIM4_Int_Init(30240-1,10-1); // 定时器初始化
TIM13_PWM_Init(500-1,84-1); // PWM初始化
IWDG_Init(4, 1250); // 看门狗重新初始化
2.2.3 状态机重置:
- 系统状态变量被重置为初始值:
g_system_state = SYS_STATE_IDLE; // 状态机回到空闲状态
2.2.4 RAM数据丢失:
- 所有存储在RAM中的变量(如`g_timeout_counter`)都会丢失,除非被定义为保留区域变量
- 全局变量会被重新初始化为其定义时的值
2.3 实际效果示例
假设系统在运行过程中出现了以下异常情况:
2.3.1 通信死锁场景:
- 系统在等待Modbus响应时卡住在`SYS_STATE_PROCESSING`状态
- 超过2秒没有返回主循环,无法执行`IWDG_Feed()`
- IWDG计数器减至0,触发系统复位
- 系统重启,重新初始化所有外设,状态机回到`SYS_STATE_IDLE`
- 通信模块被重新初始化,之前的死锁状态被清除
2.3.2 舵机控制异常场景:
- 舵机控制函数陷入死循环,无法返回主循环
- 超过2秒无法喂狗,IWDG触发复位
- 系统重启后,舵机控制模块被重新初始化
- 可能需要重新发送舵机控制命令,但系统已恢复正常运行能力
2.3.3 程序跑飞场景:
- 由于电磁干扰或其他原因,程序计数器指向了非法地址
- 系统执行了非预期的代码,无法正常喂狗
- IWDG触发复位,系统重新启动
- 程序计数器被重置为正确的启动地址,系统恢复正常执行
2.4 复位的可观察现象
当IWDG触发系统复位时,可以观察到以下现象:
1.硬件指示:
- 如果板子上有电源指示灯,可能会短暂闪烁
- 连接的外设(如LED)会回到初始状态
2.通信中断:
- 所有通信接口(UART、SPI等)会短暂中断
- 正在进行的通信会被打断,需要重新建立
3.调试观察:
- 如果连接了调试器,会看到程序执行突然跳回到启动代码
- 调试会话可能会断开,需要重新连接
4.系统行为:
- 系统会执行启动序列,包括初始化代码
- 所有外设会被重新配置
- 状态机会回到初始状态
2.5在项目中的实际应用价值
IWDG触发系统复位在蓝牙夹具项目中具有以下价值:
1. 自动恢复能力:
- 系统无需人工干预即可从大多数软件故障中恢复
- 特别适合无人值守的工业应用场景
2.防止系统长时间不响应:
- 确保系统不会永久性地卡在某个错误状态
- 最长不响应时间被限制在IWDG超时时间内(本项目为2秒)
3.提高系统可靠性:
- 即使在恶劣环境(如电磁干扰)下,系统也能自动恢复
- 减少因软件bug导致的系统完全失效情况
4.简化错误处理:
- 不需要为每种可能的错误情况编写恢复代码
- IWDG提供了一种通用的"最后手段"恢复机制
总的来说,IWDG触发系统复位是一种强大的故障恢复机制,它通过完全重启系统来清除任何异常状态,使系统回到一个已知的、可预测的初始状态,从而确保系统的长期稳定运行。在蓝牙夹具项目中,这种机制对于提高系统的可靠性和鲁棒性至关重要。
3.状态机架构设计
代码中实现了一个典型的有限状态机(FSM)架构,用于管理系统的不同工作状态。
状态定义:
// 系统状态枚举
typedef enum {
SYS_STATE_INIT, // 初始化状态
SYS_STATE_IDLE, // 空闲状态
SYS_STATE_PROCESSING, // 处理命令状态
SYS_STATE_ERROR, // 错误状态
SYS_STATE_RECOVERY // 恢复状态
} SystemState;
// 系统错误码枚举
typedef enum {
SYS_ERROR_NONE, // 无错误
SYS_ERROR_COMM, // 通信错误
SYS_ERROR_TIMEOUT, // 超时错误
SYS_ERROR_SERVO // 舵机错误
} SystemError;
状态机实现:
switch(g_system_state) {
case SYS_STATE_INIT:
// 初始化过程
g_system_state = SYS_STATE_IDLE;
break;
case SYS_STATE_IDLE:
// 空闲状态,处理接收到的Modbus命令
g_system_state = SYS_STATE_PROCESSING;
g_timeout_counter = 0; // 重置超时计数器
break;
case SYS_STATE_PROCESSING:
// 处理Modbus命令
// ...超时检测...
break;
case SYS_STATE_ERROR:
// 错误处理
// ...根据错误类型执行不同的恢复策略...
g_system_state = SYS_STATE_RECOVERY;
break;
case SYS_STATE_RECOVERY:
// 系统恢复处理
g_system_error = SYS_ERROR_NONE;
g_system_state = SYS_STATE_IDLE;
break;
default:
g_system_state = SYS_STATE_IDLE;
break;
}
这种状态机架构的优点:
1. 清晰的状态划分,每个状态有明确的职责
2. 状态转换逻辑明确,便于调试和维护
3. 错误处理机制完善,能够从错误状态恢复
4. 超时检测机制,防止系统卡在某个状态
4. Modbus的CRC校验实现
Modbus协议使用CRC-16校验来确保数据传输的完整性。在代码中,有三种CRC16校验实现:
标准的CRC16实现:
unsigned int GetCRC16(unsigned char *ptr, unsigned char len)
{
uint16_t index;
uint8_t crcl = 0xFF; // CRC低字节
uint8_t crch = 0xFF; // CRC高字节
uint8_t 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
} ;
u8 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
} ;
while (len--) // 计算指针所指向的数据长度
{
index = crcl ^ *ptr++;
crcl = crch ^ TabH[index];
crch = TabL[index];
}
return ((crcl<<8) | crch);
};
其他两个CRC16变种实现:
//crc校验,用于校验高字节和低字节
unsigned int GetCRC16_L(unsigned char *ptr, unsigned char len)
{
uint16_t index;
uint8_t crcl = 0xFF; // CRC低字节
uint8_t crch = 0xFF; // CRC高字节
uint8_t 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
} ;
u8 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
} ;
while (len--) // 计算指针所指向的数据长度
{
index = crcl ^ *ptr++;
crcl = crch ^ TabH[index];
crch = TabL[index];
}
return crcl ;
};
unsigned int GetCRC16_H(unsigned char *ptr, unsigned char len)
{
uint16_t index;
uint8_t crcl = 0xFF; // CRC低字节
uint8_t crch = 0xFF; // CRC高字节
uint8_t 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
} ;
u8 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
} ;
while (len--) // 计算指针所指向的数据长度
{
index = crcl ^ *ptr++;
crcl = crch ^ TabH[index];
crch = TabL[index];
}
return crch ;
};
Modbus服务函数,CRC校验在通信中的应用:
//Modbus处理函数,用于处理接收到的数据(由主程序循环调用)
u8 Modbus_Service(void)
{
u16 recCRC;
// 如果没有接收到完整的帧,则返回1表示还在等待
if(Modbus_FrameFlag != 1)
return 1;
// CRC校验
calCRC = GetCRC16(Modbus_RX_BUFF, Modbus_RX_LEN-2);
recCRC = Modbus_RX_BUFF[Modbus_RX_LEN-1]|(((u16)Modbus_RX_BUFF[Modbus_RX_LEN-2])<<8);
if(calCRC != recCRC) {
// CRC校验错误,清空接收缓冲区并返回错误码
Modbus_FrameFlag = 0;
Modbus_RX_CNT = 0;
Modbus_RX_LEN = 0;
return 2; // 错误码:CRC校验错误
}
// 地址检验
if(Modbus_RX_BUFF[0] != Modbus_Addr) {
// 地址不匹配,清空接收缓冲区并返回错误码
Modbus_FrameFlag = 0;
Modbus_RX_CNT = 0;
Modbus_RX_LEN = 0;
return 3; // 错误码:地址不匹配
}
// 处理功能码
startRegAddr = (((u16)Modbus_RX_BUFF[2])<<8)|Modbus_RX_BUFF[3]; // 获取寄存器起始地址
// 根据功能码执行相应操作
switch(Modbus_RX_BUFF[1]) {
case 03: // 读多个寄存器
Modbus_03_Solve();
break;
case 06: // 写单个寄存器
Modbus_06_Solve();
break;
case 05: // 写单个线圈
Modbus_05_Solve();
break;
default:
// 不支持的功能码
Modbus_FrameFlag = 0;
Modbus_RX_CNT = 0;
Modbus_RX_LEN = 0;
return 4; // 错误码:不支持的功能码
}
// 处理完毕,清空标志和计数器
Modbus_FrameFlag = 0;
Modbus_RX_CNT = 0;
Modbus_RX_LEN = 0;
// 返回0表示处理成功
return 0;
}
Modbus数据发送函数:
// 发送n个字节的数据
// buff: 要发送的数据缓冲区
// len: 要发送的字节数
void Modbus_SendData(u8 *buff,u8 len)
{
u8 t;
for(t=0;t<len;t++)
{
while(USART_GetFlagStatus(MODUBS_UART,USART_FLAG_TC)==RESET);
USART_SendData(MODUBS_UART,buff[t]);
}
while(USART_GetFlagStatus(MODUBS_UART,USART_FLAG_TC)==RESET);
}
4.1 Modbus CRC校验详细分析
Modbus协议使用CRC-16校验来确保数据传输的完整性。在代码中,有三个CRC16相关函数:
1. GetCRC16: 计算完整的16位CRC校验值
2. GetCRC16_L: 只返回CRC校验值的低字节
3. GetCRC16_H: 只返回CRC校验值的高字节
4.2 CRC-16校验算法原理
CRC-16校验是一种循环冗余校验算法,用于检测数据传输过程中的错误。其工作原理如下:
1. 初始化CRC寄存器为0xFFFF(高低字节都是0xFF)
2. 对每个数据字节执行以下操作:
- 将数据字节与CRC低字节进行异或运算,得到一个索引值
- 根据索引值从预定义的查找表中获取新的CRC高低字节值
- 更新CRC寄存器
代码实现分析
unsigned int GetCRC16(unsigned char *ptr, unsigned char len)
{
uint16_t index;
uint8_t crcl = 0xFF; // CRC低字节初始化为0xFF
uint8_t crch = 0xFF; // CRC高字节初始化为0xFF
uint8_t TabH[] = { /* CRC高字节查找表 */ };
uint8_t TabL[] = { /* CRC低字节查找表 */ };
while (len--) // 遍历所有数据字节
{
index = crcl ^ *ptr++; // 数据字节与CRC低字节异或,得到索引
crcl = crch ^ TabH[index]; // 更新CRC低字节
crch = TabL[index]; // 更新CRC高字节
}
return ((crcl<<8) | crch); // 返回16位CRC值
}
在Modbus通信中的应用:
// CRC校验
calCRC = GetCRC16(Modbus_RX_BUFF, Modbus_RX_LEN-2); // 计算接收数据的CRC值
recCRC = Modbus_RX_BUFF[Modbus_RX_LEN-1]|(((u16)Modbus_RX_BUFF[Modbus_RX_LEN-2])<<8); // 获取接收数据中的CRC值
if(calCRC != recCRC) {
// CRC校验错误,清空接收缓冲区并返回错误码
Modbus_FrameFlag = 0;
Modbus_RX_CNT = 0;
Modbus_RX_LEN = 0;
return 2; // 错误码:CRC校验错误
}
4.3 为什么需要三个CRC函数?
1. GetCRC16: 用于完整的CRC校验,返回16位CRC值
2. GetCRC16_L: 只返回CRC低字节,可能用于某些只需要低字节校验的场景
3. GetCRC16_H: 只返回CRC高字节,可能用于某些只需要高字节校验的场景
这种设计提供了灵活性,允许根据不同的通信需求使用不同的CRC校验方式。
main.c代码
#include "sys.h"
#include "delay.h"
#include "pwm.h"
#include "fashion_star_uart_servo.h"
#include "fashion_star_uart_servo_examples.h"
#include "ring_buffer.h"
#include "servo_move.h"
#include "usart2.h"
#include "sys_tick.h"
#include "uart4.h"
#include "timer.h"
#include "modbus.h" // 确保在使用Modbus相关函数和变量前包含
#include "stdlib.h"
#include "stdio.h"
#include "iwdg.h" // 独立看门狗头文件
extern u16 tim_value;
// 系统状态枚举
typedef enum {
SYS_STATE_INIT, // 初始化状态
SYS_STATE_IDLE, // 空闲状态
SYS_STATE_PROCESSING, // 处理命令状态
SYS_STATE_ERROR, // 错误状态
SYS_STATE_RECOVERY // 恢复状态
} SystemState;
// 系统错误码枚举
typedef enum {
SYS_ERROR_NONE, // 无错误
SYS_ERROR_COMM, // 通信错误
SYS_ERROR_TIMEOUT, // 超时错误
SYS_ERROR_SERVO // 舵机错误
} SystemError;
// 系统状态变量
SystemState g_system_state = SYS_STATE_INIT;
SystemError g_system_error = SYS_ERROR_NONE;
u16 g_timeout_counter = 0;
#define MAX_TIMEOUT_COUNT 1000 // 最大超时时间计数
int main(void)
{
tim_value=20;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
delay_init(168); //延时初始化
Usart_Init(); //串口1初始化波特率为9600,串口2 115200
uart4_init(9600);
LED_Init();
TIM3_Int_Init(10000-1,8400-1);//1S一次中断
TIM4_Int_Init(30240-1,10-1);
TIM13_PWM_Init(500-1,84-1); //84M/21=4Mhz的计数频率,计数到500为500次,所以PWM频率为 4M/500=8Khz.
// 初始化独立看门狗,2秒超时
IWDG_Init(4, 1250); // 2秒的超时时间
// 初始化系统状态
g_system_state = SYS_STATE_IDLE;
while(1)
{
// 喂狗
IWDG_Feed();
// 根据当前系统状态执行不同的操作
switch(g_system_state)
{
case SYS_STATE_INIT:
// 初始化过程,如有需要
g_system_state = SYS_STATE_IDLE;
break;
case SYS_STATE_IDLE:
// 空闲状态,处理接收到的Modbus命令
g_system_state = SYS_STATE_PROCESSING;
g_timeout_counter = 0; // 重置超时计数器
break;
case SYS_STATE_PROCESSING:
// 处理Modbus命令
if(Modbus_Service() == 0) {
// 命令处理成功
g_system_state = SYS_STATE_IDLE;
} else {
// 超时检测
g_timeout_counter++;
if(g_timeout_counter > MAX_TIMEOUT_COUNT) {
// 处理超时
g_system_error = SYS_ERROR_TIMEOUT;
g_system_state = SYS_STATE_ERROR;
}
}
timer_stop_s(tim_value);
break;
case SYS_STATE_ERROR:
// 错误处理
// 根据错误类型执行不同的恢复策略
switch(g_system_error) {
case SYS_ERROR_COMM:
// 通信错误处理
break;
case SYS_ERROR_TIMEOUT:
// 超时错误处理
// 例如重置通信缓冲区
Rec_Buf_clean(Modbus_RX_BUFF, 200);
Modbus_RX_CNT = 0;
Modbus_RX_LEN = 0;
Modbus_FrameFlag = 0;
break;
case SYS_ERROR_SERVO:
// 舵机错误处理
break;
default:
break;
}
// 切换到恢复状态
g_system_state = SYS_STATE_RECOVERY;
break;
case SYS_STATE_RECOVERY:
// 系统恢复处理
// 例如闪烁LED以指示系统正在恢复
LED0 = !LED0;
delay_ms(500);
// 重置错误并返回空闲状态
g_system_error = SYS_ERROR_NONE;
g_system_state = SYS_STATE_IDLE;
break;
default:
// 未知状态处理
g_system_state = SYS_STATE_IDLE;
break;
}
// 为了不让CPU负担过重
delay_ms(10);
}
}
modbus.c代码
#include "modbus.h"
#include "pwm.h"
u8 Modbus_Addr=0x01; // 从机地址
u8 Modbus_RX_BUFF[200]; // 接收缓冲区
u16 Modbus_RX_CNT=0; // 接收计数器
u16 Modbus_RX_EN=0; // 接收延时计时
u8 Modbus_FrameFlag=0; // 帧接收完成
u16 calCRC; // CRC校验值
u16 startRegAddr; // 起始寄存器地址
u16 RegNum; // 寄存器数量
u8 Modbus_TX_BUFF[200]; // 发送缓冲区
u16 Modbus_RX_LEN=0; // 接收数据长度
u32 Modbus_Tx_ANGLE=0; // 用于存储发送的角度
u16 tim_value;
u16 stop_time; // 停止时间
unsigned int GetCRC16(unsigned char *ptr, unsigned char len)
{
uint16_t index;
uint8_t crcl = 0xFF; // CRC低字节
uint8_t crch = 0xFF; // CRC高字节
uint8_t 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
} ;
u8 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
} ;
while (len--) // 计算指针所指向的数据长度
{
index = crcl ^ *ptr++;
crcl = crch ^ TabH[index];
crch = TabL[index];
}
return ((crcl<<8) | crch);
};
//crc校验,用于校验高字节和低字节
unsigned int GetCRC16_L(unsigned char *ptr, unsigned char len)
{
uint16_t index;
uint8_t crcl = 0xFF; // CRC低字节
uint8_t crch = 0xFF; // CRC高字节
uint8_t 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
} ;
u8 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
} ;
while (len--) // 计算指针所指向的数据长度
{
index = crcl ^ *ptr++;
crcl = crch ^ TabH[index];
crch = TabL[index];
}
return crcl ;
};
unsigned int GetCRC16_H(unsigned char *ptr, unsigned char len)
{
uint16_t index;
uint8_t crcl = 0xFF; // CRC低字节
uint8_t crch = 0xFF; // CRC高字节
uint8_t 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
} ;
u8 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
} ;
while (len--) // 计算指针所指向的数据长度
{
index = crcl ^ *ptr++;
crcl = crch ^ TabH[index];
crch = TabL[index];
}
return crch ;
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Modbus函数,用于处理接收到的数据(由主程序循环调用)
u16 Modbus_IO[100]; // 用于存储寄存器地址的数组
//////////////////////////////////////////////////////////////////////////////
// 发送n个字节的数据
// buff: 要发送的数据缓冲区
// len: 要发送的字节数
void Modbus_SendData(u8 *buff,u8 len)
{
u8 t;
for(t=0;t<len;t++)
{
while(USART_GetFlagStatus(MODUBS_UART,USART_FLAG_TC)==RESET);
USART_SendData(MODUBS_UART,buff[t]);
}
while(USART_GetFlagStatus(MODUBS_UART,USART_FLAG_TC)==RESET);
}
/////////////////////////////////////////////////////////////////////////////////////
//Modbus处理函数,用于处理接收到的数据(由主程序循环调用)
u8 Modbus_Service(void)
{
u16 recCRC;
// 如果没有接收到完整的帧,则返回1表示还在等待
if(Modbus_FrameFlag != 1)
return 1;
// CRC校验
calCRC = GetCRC16(Modbus_RX_BUFF, Modbus_RX_LEN-2);
recCRC = Modbus_RX_BUFF[Modbus_RX_LEN-1]|(((u16)Modbus_RX_BUFF[Modbus_RX_LEN-2])<<8);
if(calCRC != recCRC) {
// CRC校验错误,清空接收缓冲区并返回错误码
Modbus_FrameFlag = 0;
Modbus_RX_CNT = 0;
Modbus_RX_LEN = 0;
return 2; // 错误码:CRC校验错误
}
// 地址检验
if(Modbus_RX_BUFF[0] != Modbus_Addr) {
// 地址不匹配,清空接收缓冲区并返回错误码
Modbus_FrameFlag = 0;
Modbus_RX_CNT = 0;
Modbus_RX_LEN = 0;
return 3; // 错误码:地址不匹配
}
// 处理功能码
startRegAddr = (((u16)Modbus_RX_BUFF[2])<<8)|Modbus_RX_BUFF[3]; // 获取寄存器起始地址
// 根据功能码执行相应操作
switch(Modbus_RX_BUFF[1]) {
case 03: // 读多个寄存器
Modbus_03_Solve();
break;
case 06: // 写单个寄存器
Modbus_06_Solve();
break;
case 05: // 写单个线圈
Modbus_05_Solve();
break;
default:
// 不支持的功能码
Modbus_FrameFlag = 0;
Modbus_RX_CNT = 0;
Modbus_RX_LEN = 0;
return 4; // 错误码:不支持的功能码
}
// 处理完毕,清空标志和计数器
Modbus_FrameFlag = 0;
Modbus_RX_CNT = 0;
Modbus_RX_LEN = 0;
// 返回0表示处理成功
return 0;
}
// 将浮点数转换为十六进制
u32 Float_translate_to_hex(float hex)
{
return *(float*)&hex;
}
// 将十六进制转换为整数
u16 hex_translate_TO_int_single(u8 hex)
{
u16 sum=0;
u16 mul=1;
int i;
int r;
int count=1;
do{
r=hex%16;
for(i=0;i<count;i++)
mul*=16;
mul*=r;
sum+=mul;
mul=1;
count++;
}while(hex/=16);
return sum;
}
u16 Final_num(u16 hex_L,u16 hex_H)
{
u16 num_H;
u16 num_L;
u16 final_in_num;
num_L=hex_translate_TO_int_single(hex_L);
num_H=hex_translate_TO_int_single(hex_H);
final_in_num=num_H*256+num_L;
return final_in_num;
}
// 03命令实现读取角度
void Modbus_03_Solve(void)
{
// 读取角度和角度
u8 num_h;
u8 num_l;
u8 Angle_buf[8]={0x01,0x03,0x00,0x04,0x00,0x00,0x00,0x00};
u8 calCRC_H;
u8 calCRC_L;
float curRead_angle; // 读取当前的角度
curRead_angle=FSUSExample_ReadeServoAngle();
Modbus_Tx_ANGLE=Float_translate_to_hex(curRead_angle);
num_l=(Modbus_Tx_ANGLE&0x00ff);
num_h=(Modbus_Tx_ANGLE&0xff00);
Angle_buf[4]=num_l;
Angle_buf[5]=num_h;
calCRC_L = GetCRC16_L(Angle_buf,6);
calCRC_H = GetCRC16_H(Angle_buf,6);
Angle_buf[6]=(calCRC_L&0x00ff);// 低字节
Angle_buf[7]=(calCRC_H&0x00ff);// 高字节
for(int t=0;t<8;t++)
{
USART_SendData(USART1,Angle_buf[t]); // 向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
delay_ms(500);
}
// 05命令实现写入线圈寄存器的命令程序改为可以转
extern u16 Status;
extern u16 Counter;
void Modbus_05_Solve(void)
{
u8 main_move_Down[8] = {0x01,0x05,0x00,0x01,0xFF,0x00,0xDD,0xFA};
u8 main_move_Up[8] = {0x01,0x05,0x00,0x01,0x00,0x00,0x9C,0x0A};
u8 main_move_Stop[8] = {0x01,0x05,0x00,0x02,0xFF,0x00,0x2D,0xFA};
u8 main_move_Star[8] = {0x01,0x05,0x00,0x02,0x00,0x00,0x6C,0x0A};
u8 servo_move_Open[8] = {0x01,0x05,0x00,0x05,0xFF,0x00,0x9C,0x3B};
u8 servo_move_Close[8] = {0x01,0x05,0x00,0x06,0xFF,0x00,0x6C,0x3B};
u8 servo_error_feedback[8] = {0x01,0x05,0x00,0xFF,0xFF,0x00,0xFF,0xFF}; // 错误反馈
u8 result = 0; // 操作结果
if(0x01==Modbus_RX_BUFF[3])
{
if(0xff==Modbus_RX_BUFF[4])
{
GPIO_ResetBits(GPIOB,GPIO_Pin_5);//正转
for(int t=0;t<8;t++)
{
USART_SendData(USART1,main_move_Down[t]); // 向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
delay_ms(500);
}
else if(0x00==Modbus_RX_BUFF[4])
{
GPIO_SetBits(GPIOB,GPIO_Pin_5);//反转 同时
for(int t=0;t<8;t++)
{
USART_SendData(USART1,main_move_Up[t]); // 向串口4发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
delay_ms(500);
}
}
else if(0x02==Modbus_RX_BUFF[3])
{
if(0xff==Modbus_RX_BUFF[4])
{
GPIO_ResetBits(GPIOB,GPIO_Pin_3);//停止 选一个接口
Status = 0;
Counter = 0;
for(int t=0;t<8;t++)
{
USART_SendData(USART1,main_move_Stop[t]); // 向串口4发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
delay_ms(500);
}
else if(0x00==Modbus_RX_BUFF[4])
{
GPIO_SetBits(GPIOB,GPIO_Pin_3);//启动 同时
Status = 1;
for(int t=0;t<8;t++)
{
USART_SendData(USART1,main_move_Star[t]); // 向串口4发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
delay_ms(500);
}
}
else if(0x05==Modbus_RX_BUFF[3])
{
//0->90
result = servo_move_to_90();
if(result == 0) {
// 操作成功,发送正常反馈
for(int t=0;t<8;t++)
{
USART_SendData(USART1,servo_move_Open[t]); // 向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
delay_ms(500);
} else {
// 操作失败,发送错误反馈
// 可以根据result的不同值设置不同的错误反馈
servo_error_feedback[3] = 0x05; // 设置为当前命令
servo_error_feedback[4] = result; // 使用错误码
// 重新计算CRC (省略实际计算过程,实际应用中需要计算)
for(int t=0;t<8;t++)
{
USART_SendData(USART1,servo_error_feedback[t]); // 向串口1发送错误反馈
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
delay_ms(500);
}
}
else if(0x06==Modbus_RX_BUFF[3])
{
//90->0
result = servo_move_to_0();
if(result == 0) {
// 操作成功,发送正常反馈
for(int t=0;t<8;t++)
{
USART_SendData(USART1,servo_move_Close[t]); // 向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
delay_ms(500);
} else {
// 操作失败,发送错误反馈
servo_error_feedback[3] = 0x06; // 设置为当前命令
servo_error_feedback[4] = result; // 使用错误码
// 重新计算CRC (省略实际计算过程,实际应用中需要计算)
for(int t=0;t<8;t++)
{
USART_SendData(USART1,servo_error_feedback[t]); // 向串口1发送错误反馈
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
delay_ms(500);
}
}
}
// 06命令实现写入寄存器的命令程序改为可以写
void Modbus_06_Solve(void)
{
u8 Speed_01[8] = {0x01,0x06,0x00,0x03,0x01,0x00,0xB8,0x0A};
u8 Speed_02[8] = {0x01,0x06,0x00,0x03,0x02,0x00,0xF8,0x0B};
u8 Speed_03[8] = {0x01,0x06,0x00,0x03,0x03,0x00,0x39,0xCB};
u8 Speed_04[8] = {0x01,0x06,0x00,0x03,0x04,0x00,0x78,0x09};
u8 Speed_05[8] = {0x01,0x06,0x00,0x03,0x05,0x00,0xB9,0xC9};
u8 Max_time[8];
if(0x07==Modbus_RX_BUFF[3])//01 06 00 07 00 25 使用
{
stop_time = Modbus_RX_BUFF[4] *256 + Modbus_RX_BUFF[5];
// 将十进制值转换为tim_value
tim_value=stop_time;
for(int t=0;t<8;t++)
{
Max_time[t]=Modbus_RX_BUFF[t];
}
for(int t=0;t<8;t++)
{
USART_SendData(USART1,Max_time[t]); // 向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
delay_ms(500);
}
switch(Modbus_RX_BUFF[5])
{
case 01:
{
// 第一个速度
TIM_SetCompare1(TIM13,273);//400
Status = 1;
for(int t=0;t<8;t++)
{
USART_SendData(USART1,Speed_01[t]); // 向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
delay_ms(500);
break;
}
case 02:
{
// 第二个速度
TIM_SetCompare1(TIM13,250);//350
Status = 1;
for(int t=0;t<8;t++)
{
USART_SendData(USART1,Speed_02[t]); // 向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
delay_ms(500);
break;
}
case 03:
{
// 第三个速度
TIM_SetCompare1(TIM13,230);//300
Status = 1;
for(int t=0;t<8;t++)
{
USART_SendData(USART1,Speed_03[t]); // 向串口4发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
delay_ms(500);
break;
}
case 04:
{
// 第四个速度
TIM_SetCompare1(TIM13,200);//280
Status = 1;
for(int t=0;t<8;t++)
{
USART_SendData(USART1,Speed_04[t]); // 向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
delay_ms(500);
break;
}
case 05:
{
// 第五个速度
TIM_SetCompare1(TIM13,180);//250
Status = 1;
for(int t=0;t<8;t++)
{
USART_SendData(USART1,Speed_05[t]); // 向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
delay_ms(500);
break;
}
}
}
void Rec_Buf_clean(u8 *point,u8 len ) // 清除缓冲区
{
int p;
for (p=0;p<len;p++)
{
point[p]=0;
}
}
// 判断接收缓冲区是否满,如果满则清除缓冲区
void Modbus_zubao(uint8_t Res)
{
if(Modbus_RX_CNT<200)
{
Modbus_RX_BUFF[Modbus_RX_CNT++]=Res;
Modbus_RX_EN = 0;
}
else
{
Rec_Buf_clean(Modbus_RX_BUFF,200);
Modbus_RX_CNT = 0;
}
}
// 判断接收延时是否超时,如果超时则接收完成
#define Modbus_RX_EN_MAX 18
void Modbus_Is_jieshou(void)
{
Modbus_RX_EN++;
if((Modbus_RX_EN>Modbus_RX_EN_MAX) &&(Modbus_RX_CNT!=0))
{
Modbus_RX_LEN = Modbus_RX_CNT;
Modbus_FrameFlag=1;//高字节帧接收完成
Modbus_RX_EN = 0;
}
}

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)