# 介绍
HART 协议模块是 C 语言编写的,使用者无需对 HART 协议了解,通过接口文档可以快速完成应用层协议开发。
模块可以分别被主机和从机使用,调用少量接口完成开发,模块接口测试覆盖率 100%。
# 架构
## 架构说明
HART 协议模块是设备与主机之间的通信协议,主机和从机之间通过串口通信,主机通过串口发送命令,从机接收命令并返回响应数据。因此,主机和从机都需要使用 HART 协议模块,主机和从机的接口不同,主机接口主要是发送命令,从机接口主要是接收命令并返回响应数据。
考虑到移植性在设计阶段模块因避免使用操作系统相关的接口,模块接口使用回调函数的方式实现,主机和从机需要实现不同的回调函数,主机需要实现发送数据的回调函数,从机需要实现接收数据的回调函数。
# 内容说明
1. 7.9 版本认证支持命令 78、79、534
2. safeHART 数字化安全连锁,一种通讯协议,主要是对数据部分增加了 CRC 校验和序列号,打开 Burst-Mode 后发送的命令发送的是 safeHART 命令。
https://library.fieldcommgroup.org/20085/TS20085/4.0/#page=36
3. 2024 年 3 月 8 日后提交测试必须是 3.8 版本测试
4. 新增 548-553 指令
# 软件设计
## 结构图
│ hart.c
│ hart_cache.c
│ hart_frame.c
│ readme.md
├─inc
│ hart.h
│ hart_common_tables_specification.h
│ hart_frame.h
│ hart_frame_user.h
│
├─lib
│ ├─flow
│ │ example.c
│ │ flow.h
│ │ flow_core.c
│ │ flow_core.h
│ │ flow_def.h
│ │ flow_sem.h
│ │ README.md
│ │
│ ├─inc
│ │ data_type_def.h
│ │ debug.h
│ │ lib.h
│ │ log.h
│ │ malloc.h
│ │ osel_arch.h
│ │ sqqueue.h
│ │
│ └─src
│ debug.c
│ lib.c
│ malloc.c
│ sqqueue.c
│
├─master
│ ├─inc
│ │ hart_master.h
│ │ hart_master_frame.h
│ │ hart_master_req.h
│ │ hart_master_rsp.h
│ │
│ └─src
│ hart_master.c
│ hart_master_frame.c
│ hart_master_req.c
│ hart_master_req_user.c
│ hart_master_rsp.c
│
├─public
│
└─slave
├─inc
│ hart_slave.h
│ hart_slave_frame.h
│ hart_slave_req.h
│
└─src
hart_slave.c
hart_slave_frame.c
hart_slave_req.c
hart_slave_req_user.c
## 组织结构
## 模块说明
| 文件 | 路径 |
2. 添加宏定义和头文件
宏定义:STM32,SLAVE
头文件路径
3. HART 协议和应用层的中间层
该文件主要实现 HART 模块需要的和 MCU 相关的接口,例如串口发送、Flash 读写等。
hart_user_data_refush(): 刷新用户实时数据,包括模拟量和数字量。
response(uint8_t uart_index, uint8_t *data, uint16_t len): 发送数据(hart 协议数据帧)。
flash_read(uint32_t addr, uint8_t *data, uint16_t len): flash 读取接口。
flash_write(uint32_t addr, uint8_t *data, uint16_t len): flash 写入接口。
perform_self_test(void): 执行自检。
device_reset(void): 设备复位。
squawk_control(BOOL open, uint8_t second): 设备呼叫,0 一直呼叫,1-255 秒。
armed(void): 技术人员按下一个特殊的按钮或按钮组合,指示从机应响应 command74。
set_dynamics(device_variable_dynamics_t *const dynamics): 设置动态变量。
hart_rx_cb(uint8_t uart_index, uint8_t \*data, uint16_t len): 串口 1、5 接收中断回调函数。
hart_tx_complete_cb(void): 串口 1 发送完成回调函数。
hart_uart_init(void): 串口 1 初始化。
hart_uart_dinit(void): 串口 1 去初始化。
hart_ble_init(void): BLE 初始化。
hart_ble_dinit(void): BLE 去初始化。
hart_init(void): HART 初始化。
hart_dinit(void): HART 去初始化。
hart_task(void): HART 任务。
源码实现:
```
/*
* @Author: xxx
* @Date: 2023-08-02 08:28:36
* @LastEditors: xxx
* @LastEditTime: 2023-08-23 14:15:44
* @Description: 此文件主要实现板卡的HART功能
* Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#include "app.h"
#include "hart.h"
#include "hart_frame.h"
#include "uarts.h"
#include "test_bsp.h"
#define HART_UART1 USART1
#define HART_UART2 USART5
#define UART_RXSIZE (240u)
#define UART_TXSIZE (240u)
uart_t *uarts[APP_UART_MAX];
app_dynamics_t app_dynamics;
static __IO BOOL hart_idle = TRUE;
static void hart_user_data_refush(void)
{
app_analog_quantity_t *analog_quantity = &app_dynamics.analog_quantity;
app_digital_quantity_t *digital_quantity = &app_dynamics.digital_quantity;
// 模拟量
analog_quantity->input_current = adc_raw[ADC_LOOP_CHANNEL]; // 输入电流
analog_quantity->valve_feedback = adc_raw[ADC_PSB_CHANNEL]; // 阀门反馈
analog_quantity->atmospheric_pressure_source = adc_raw[ADC_BP_CHANNEL]; // 气压源压力
analog_quantity->pressure_at_port_a = 0; // A口压力
analog_quantity->pressure_at_port_b = 0; // B口压力
analog_quantity->amplifier_circuit = adc_raw[ADC_IPSB_CHANNEL]; // 放大器回路
analog_quantity->built_in_temperature = 0; // 内置温度
analog_quantity->ip_output_detection = rt_data.ip_output; // IP输出检测
analog_quantity->valve_percentage = actual_travel; // 阀位百分比
// 数字量
digital_quantity->input_current = loop_current; // 输入电流
digital_quantity->valve_feedback = adc_raw[ADC_PSB_CHANNEL]; // 阀门反馈
digital_quantity->atmospheric_pressure_source = adc_raw[ADC_BP_CHANNEL]; // 气压源压力
digital_quantity->pressure_at_port_a = 0; // A口压力
digital_quantity->pressure_at_port_b = 0; // B口压力
digital_quantity->amplifier_circuit = adc_raw[ADC_IPSB_CHANNEL]; // 放大器回路
digital_quantity->built_in_temperature = 0; // 内置温度
digital_quantity->ip_output_detection = rt_data.ip_output; // IP输出检测
digital_quantity->target_row = target_travel; // 目标行程
digital_quantity->current_row = actual_travel; // 实际行程
digital_quantity->friction = 0; // 摩擦力
digital_quantity->spring_force = 0; // 弹簧力
}
/**
* @brief 发送数据(hart协议数据帧)
* @param {uint8_t} *txBuf
* @param {uint16_t} len
* @return {*}
*/
static void response(uint8_t uart_index, uint8_t *data, uint16_t len)
{
#ifdef STM32
if (uart_index == APP_UART_1)
{
HART_CD_OFF(); // 因为DMA发送调用uart_send_data接口后会立即退出,不需要等待发送是否成功,因此HART_CD_ON需要在DMA发送完成中断中调用
}
uart_send_data(uarts[uart_index], data, len);
#else
LOG_HEX(data, len);
#endif
}
/**
* @brief flash读取接口
* @param {uint32_t} addr
* @param {uint8_t} *data
* @param {uint16_t} len
* @return {*}
*/
static BOOL flash_read(uint32_t addr, uint8_t *data, uint16_t len)
{
m95_1_normal_read(addr, data, len);
return TRUE;
}
/**
* @brief flash写入接口
* @param {uint32_t} addr
* @param {uint8_t} *data
* @param {uint16_t} len
* @return {*}
*/
static BOOL flash_write(uint32_t addr, uint8_t *data, uint16_t len)
{
m95_1_normal_write(addr, data, len);
return TRUE;
}
/**
* @brief 执行自检
* @param {*}
* @return {*}
*/
static void perform_self_test(void)
{
}
/**
* @brief 设备复位
* @param {*}
* @return {*}
*/
static void device_reset(void)
{
}
/**
* @brief 设备呼叫,0一直呼叫,1-255秒
* @param {*}
* @return {*}
*/
static void squawk_control(BOOL open, uint8_t second)
{
}
/**
* @brief 技术人员按下一个特殊的按钮或按钮组合,指示从机应响应command74
* @param {*}
* @return {*}
*/
static BOOL armed(void)
{
return TRUE;
}
/**
* @brief 设置动态变量
* @return {*}
*/
static BOOL set_dynamics(device_variable_dynamics_t *const dynamics)
{
app_analog_quantity_t *analog_quantity = &app_dynamics.analog_quantity;
app_digital_quantity_t *digital_quantity = &app_dynamics.digital_quantity;
// 模拟量
dynamics->dynamics_user.analog_quantity.input_current = &analog_quantity->input_current; // 输入电流
dynamics->dynamics_user.analog_quantity.valve_feedback = &analog_quantity->valve_feedback; // 阀门反馈
dynamics->dynamics_user.analog_quantity.atmospheric_pressure_source = &analog_quantity->atmospheric_pressure_source; // 大气压力源
dynamics->dynamics_user.analog_quantity.pressure_at_port_a = &analog_quantity->pressure_at_port_a; // A口压力
dynamics->dynamics_user.analog_quantity.pressure_at_port_b = &analog_quantity->pressure_at_port_b; // B口压力
dynamics->dynamics_user.analog_quantity.amplifier_circuit = &analog_quantity->amplifier_circuit; // 放大器电路
dynamics->dynamics_user.analog_quantity.built_in_temperature = &analog_quantity->built_in_temperature; // 内置温度
dynamics->dynamics_user.analog_quantity.ip_output_detection = &analog_quantity->ip_output_detection; // IP输出检测
dynamics->dynamics_user.analog_quantity.valve_percentage = &analog_quantity->valve_percentage; // 阀位百分比
// 数字量
dynamics->dynamics_user.digital_quantity.input_current = &digital_quantity->input_current; // 输入电流
dynamics->dynamics_user.digital_quantity.valve_feedback = &digital_quantity->valve_feedback; // 阀门反馈
dynamics->dynamics_user.digital_quantity.atmospheric_pressure_source = &digital_quantity->atmospheric_pressure_source; // 大气压力源
dynamics->dynamics_user.digital_quantity.pressure_at_port_a = &digital_quantity->pressure_at_port_a; // A口压力
dynamics->dynamics_user.digital_quantity.pressure_at_port_b = &digital_quantity->pressure_at_port_b; // B口压力
dynamics->dynamics_user.digital_quantity.amplifier_circuit = &digital_quantity->amplifier_circuit; // 放大器电路
dynamics->dynamics_user.digital_quantity.built_in_temperature = &digital_quantity->built_in_temperature; // 内置温度
dynamics->dynamics_user.digital_quantity.ip_output_detection = &digital_quantity->ip_output_detection; // IP输出检测
dynamics->dynamics_user.digital_quantity.target_row = &digital_quantity->target_row; // 目标行程
dynamics->dynamics_user.digital_quantity.current_row = &digital_quantity->current_row; // 当前行程
dynamics->dynamics_user.digital_quantity.friction = &digital_quantity->friction; // 摩擦力
dynamics->dynamics_user.digital_quantity.spring_force = &digital_quantity->spring_force; // 弹簧力
return TRUE;
}
static BOOL common_event(hart_interface_user_event_e event, const void *const data)
{
switch (event)
{
case HART_COMMAND_257_EVENT:
// 寻找、计算P / I / D
break;
case HART_COMMAND_258_EVENT:
// 计算 摩擦力
break;
case HART_COMMAND_259_EVENT:
// 计算 弹簧力
break;
case HART_COMMAND_UPDATE_EVENT:
// 刷新用户实时数据
hart_user_data_refush();
break;
case HART_COMMAND_500_EVENT:
// 测试命令
return hart_user_test((hart_user_req_t *)data);
default:
return FALSE;
}
return TRUE;
}
// 串口1、5接收中断回调函数
static void hart_rx_cb(uint8_t uart_index, uint8_t *data, uint16_t len)
{
DBG_ASSERT(uart_index < APP_UART_MAX __DBG_LINE);
hart_idle = FALSE;
// 串口1接收HART数据,因为是空闲中断处理这里只过滤掉前导码
hart_handle(uart_index, data, len);
hart_idle = TRUE;
}
static void hart_tx_complete_cb(void)
{
// 串口1发送完成回调函数
HART_CD_ON();
}
void hart_uart_init(void)
{
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART1);
GPIO_SET_OUTPUT(HART_EN_GPIO_Port, HART_EN_Pin);
GPIO_SET_OUTPUT(HART_RST_GPIO_Port, HART_RST_Pin);
GPIO_SET_OUTPUT(HART_CD_GPIO_Port, HART_CD_Pin);
GPIO_SET_ALTERNATE(HART_TX_GPIO_Port, HART_TX_Pin);
GPIO_SET_ALTERNATE(HART_RX_GPIO_Port, HART_RX_Pin);
delay_ms(10);
// 串口1初始化开始
HART_CD_ON();
HART_RST_OFF();
delay_ms(20);
HART_RST_ON();
HART_EN_DISABLE();
HART_EN_ENABLE();
HART_CD_ON();
if (uarts[APP_UART_1] == NULL)
{
uarts[APP_UART_1] = uart_create(HART_UART1, TRUE, UART_RXSIZE, hart_rx_cb, TRUE, UART_TXSIZE, hart_tx_complete_cb);
uarts[APP_UART_1]->uart_index = APP_UART_1;
uarts[APP_UART_1]->dma = DMA1;
uarts[APP_UART_1]->dma_rx_channel = LL_DMA_CHANNEL_3;
uarts[APP_UART_1]->dma_tx_channel = LL_DMA_CHANNEL_2;
uart_recv_en(uarts[APP_UART_1]);
}
// 串口1初始化结束
}
void hart_uart_dinit(void)
{
HART_EN_DISABLE();
LL_APB2_GRP1_DisableClock(LL_APB2_GRP1_PERIPH_USART1);
LL_USART_Disable(USART1);
GPIO_SET_ANALOG(HART_EN_GPIO_Port, HART_EN_Pin);
GPIO_SET_ANALOG(HART_RST_GPIO_Port, HART_RST_Pin);
GPIO_SET_ANALOG(HART_CD_GPIO_Port, HART_CD_Pin);
GPIO_SET_ANALOG(HART_TX_GPIO_Port, HART_TX_Pin);
GPIO_SET_ANALOG(HART_RX_GPIO_Port, HART_RX_Pin);
}
void hart_ble_init(void)
{
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART5);
GPIO_SET_OUTPUT(BLE_PWR_GPIO_Port, BLE_PWR_Pin);
GPIO_SET_INPUT(BLE_STATE_GPIO_Port, BLE_STATE_Pin);
GPIO_SET_ALTERNATE(BLE_TX_GPIO_Port, BLE_TX_Pin);
GPIO_SET_ALTERNATE(BLE_RX_GPIO_Port, BLE_RX_Pin);
BLE_EN_ENABLE();
delay_ms(100);
if (uarts[APP_UART_2] == NULL)
{
uarts[APP_UART_2] = uart_create(HART_UART2, TRUE, UART_RXSIZE, hart_rx_cb, TRUE, UART_TXSIZE, NULL);
uarts[APP_UART_2]->uart_index = APP_UART_2;
uarts[APP_UART_2]->dma = DMA1;
uarts[APP_UART_2]->dma_rx_channel = LL_DMA_CHANNEL_6;
uarts[APP_UART_2]->dma_tx_channel = LL_DMA_CHANNEL_7;
uart_recv_en(uarts[APP_UART_2]);
}
}
void hart_ble_dinit(void)
{
BLE_EN_DISABLE();
LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_USART5);
LL_USART_Disable(USART5);
GPIO_SET_ANALOG(BLE_PWR_GPIO_Port, BLE_PWR_Pin);
GPIO_SET_ANALOG(BLE_STATE_GPIO_Port, BLE_STATE_Pin);
GPIO_SET_ANALOG(BLE_TX_GPIO_Port, BLE_TX_Pin);
GPIO_SET_ANALOG(BLE_RX_GPIO_Port, BLE_RX_Pin);
}
BOOL app_hart_is_idle(void)
{
return hart_idle;
}
BOOL app_hart_init(void)
{
hart_init_t init;
init.hart_protocol_version = HART_PROTOCOL_VERSION_7;
init.dir = MODULE_SLAVE;
init.interface.response = response;
init.interface.flash_read = flash_read;
init.interface.flash_write = flash_write;
init.interface.perform_self_test = perform_self_test;
init.interface.device_reset = device_reset;
init.interface.squawk_control = squawk_control;
init.interface.armed = armed;
init.interface.set_dynamics = set_dynamics;
init.interface.common_event = common_event;
uarts[APP_UART_1] = NULL;
uarts[APP_UART_2] = NULL;
// 判断当前电流 >=8mA启动蓝牙 >=3.8mA启动uart
// 注:串口初始化移动到all_flow
// hart_uart_init();
// hart_ble_init();
return hart_init(&init);
}
```