admin管理员组文章数量:1558946
一、移植
usart.c
#include "main.h"
unsigned char temp[1];
UART_HandleTypeDef huart4; //***自行修改
void debug_uart4_init(void)
{
huart4.Instance = UART4; //***自行修改
huart4.Init.BaudRate = 115200; //***自行修改
huart4.Init.WordLength = UART_WORDLENGTH_8B;
huart4.Init.StopBits = UART_STOPBITS_1;
huart4.Init.Parity = UART_PARITY_NONE;
huart4.Init.Mode = UART_MODE_TX_RX;
huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart4.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart4); //***自行修改
HAL_UART_Receive_IT(&huart4,temp,1); //***自行修改
}
void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(huart->Instance==UART4) //***自行修改
{
__HAL_RCC_UART4_CLK_ENABLE(); //***自行修改
__HAL_RCC_GPIOC_CLK_ENABLE(); //***自行修改
GPIO_InitStruct.Pin = GPIO_PIN_10; //***自行修改
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); //***自行修改
GPIO_InitStruct.Pin = GPIO_PIN_11; //***自行修改
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); //***自行修改
HAL_NVIC_SetPriority(UART4_IRQn, 1, 0); //***自行修改
HAL_NVIC_EnableIRQ(UART4_IRQn); //***自行修改
}
}
void UART4_IRQHandler(void)
{
uint8_t dr = __HAL_UART_FLUSH_DRREGISTER(&huart4); //***自行修改
protocol_data_recv(&dr, 1);
HAL_UART_IRQHandler(&huart4); //***自行修改
}
usart.h
#ifndef __BSP_USART_H
#define __BSP_USART_H
extern UART_HandleTypeDef huart4; //***自行修改
void debug_uart4_init(void);
#endif
protocol.c
#include "main.h"
#include <string.h>
struct prot_frame_parser_t
{
uint8_t *recv_ptr;
uint16_t r_oft;
uint16_t w_oft;
uint16_t frame_len;
uint16_t found_frame_head;
};
static struct prot_frame_parser_t parser;
static uint8_t recv_buf[PROT_FRAME_LEN_RECV];
uint8_t check_sum(uint8_t init, uint8_t *ptr, uint8_t len )
{
uint8_t sum = init;
while(len--)
{
sum += *ptr;
ptr++;
}
return sum;
}
static uint8_t get_frame_type(uint8_t *frame, uint16_t head_oft)
{
return (frame[(head_oft + CMD_INDEX_VAL) % PROT_FRAME_LEN_RECV] & 0xFF);
}
static uint16_t get_frame_len(uint8_t *frame, uint16_t head_oft)
{
return ((frame[(head_oft + LEN_INDEX_VAL + 0) % PROT_FRAME_LEN_RECV] << 0) |
(frame[(head_oft + LEN_INDEX_VAL + 1) % PROT_FRAME_LEN_RECV] << 8) |
(frame[(head_oft + LEN_INDEX_VAL + 2) % PROT_FRAME_LEN_RECV] << 16) |
(frame[(head_oft + LEN_INDEX_VAL + 3) % PROT_FRAME_LEN_RECV] << 24));
}
static uint8_t get_frame_checksum(uint8_t *frame, uint16_t head_oft, uint16_t frame_len)
{
return (frame[(head_oft + frame_len - 1) % PROT_FRAME_LEN_RECV]);
}
static int32_t recvbuf_find_header(uint8_t *buf, uint16_t ring_buf_len, uint16_t start, uint16_t len)
{
uint16_t i = 0;
for (i = 0; i < (len - 3); i++)
{
if (((buf[(start + i + 0) % ring_buf_len] << 0) |
(buf[(start + i + 1) % ring_buf_len] << 8) |
(buf[(start + i + 2) % ring_buf_len] << 16) |
(buf[(start + i + 3) % ring_buf_len] << 24)) == FRAME_HEADER)
{
return ((start + i) % ring_buf_len);
}
}
return -1;
}
static int32_t recvbuf_get_len_to_parse(uint16_t frame_len, uint16_t ring_buf_len,uint16_t start, uint16_t end)
{
uint16_t unparsed_data_len = 0;
if (start <= end)
unparsed_data_len = end - start;
else
unparsed_data_len = ring_buf_len - start + end;
if (frame_len > unparsed_data_len)
return 0;
else
return unparsed_data_len;
}
static void recvbuf_put_data(uint8_t *buf, uint16_t ring_buf_len, uint16_t w_oft,
uint8_t *data, uint16_t data_len)
{
if ((w_oft + data_len) > ring_buf_len)
{
uint16_t data_len_part = ring_buf_len - w_oft;
memcpy(buf + w_oft, data, data_len_part);
memcpy(buf, data + data_len_part, data_len - data_len_part);
}
else
memcpy(buf + w_oft, data, data_len);
}
static uint8_t protocol_frame_parse(uint8_t *data, uint16_t *data_len)
{
uint8_t frame_type = CMD_NONE;
uint16_t need_to_parse_len = 0;
int16_t header_oft = -1;
uint8_t checksum = 0;
need_to_parse_len = recvbuf_get_len_to_parse(parser.frame_len, PROT_FRAME_LEN_RECV, parser.r_oft, parser.w_oft);
if (need_to_parse_len < 9)
return frame_type;
if (0 == parser.found_frame_head)
{
header_oft = recvbuf_find_header(parser.recv_ptr, PROT_FRAME_LEN_RECV, parser.r_oft, need_to_parse_len);
if (0 <= header_oft)
{
parser.found_frame_head = 1;
parser.r_oft = header_oft;
if (recvbuf_get_len_to_parse(parser.frame_len, PROT_FRAME_LEN_RECV,
parser.r_oft, parser.w_oft) < 9)
return frame_type;
}
else
{
parser.r_oft = ((parser.r_oft + need_to_parse_len - 3) % PROT_FRAME_LEN_RECV);
return frame_type;
}
}
if (0 == parser.frame_len)
{
parser.frame_len = get_frame_len(parser.recv_ptr, parser.r_oft);
if(need_to_parse_len < parser.frame_len)
return frame_type;
}
if ((parser.frame_len + parser.r_oft - PROT_FRAME_LEN_CHECKSUM) > PROT_FRAME_LEN_RECV)
{
checksum = check_sum(checksum, parser.recv_ptr + parser.r_oft,
PROT_FRAME_LEN_RECV - parser.r_oft);
checksum = check_sum(checksum, parser.recv_ptr, parser.frame_len -
PROT_FRAME_LEN_CHECKSUM + parser.r_oft - PROT_FRAME_LEN_RECV);
}
else
{
checksum = check_sum(checksum, parser.recv_ptr + parser.r_oft, parser.frame_len - PROT_FRAME_LEN_CHECKSUM);
}
if (checksum == get_frame_checksum(parser.recv_ptr, parser.r_oft, parser.frame_len))
{
if ((parser.r_oft + parser.frame_len) > PROT_FRAME_LEN_RECV)
{
uint16_t data_len_part = PROT_FRAME_LEN_RECV - parser.r_oft;
memcpy(data, parser.recv_ptr + parser.r_oft, data_len_part);
memcpy(data + data_len_part, parser.recv_ptr, parser.frame_len - data_len_part);
}
else
{
memcpy(data, parser.recv_ptr + parser.r_oft, parser.frame_len);
}
*data_len = parser.frame_len;
frame_type = get_frame_type(parser.recv_ptr, parser.r_oft);
parser.r_oft = (parser.r_oft + parser.frame_len) % PROT_FRAME_LEN_RECV;
}
else
{
parser.r_oft = (parser.r_oft + 1) % PROT_FRAME_LEN_RECV;
}
parser.frame_len = 0;
parser.found_frame_head = 0;
return frame_type;
}
/**
* @brief 接收数据处理(放在串口中断服务函数中)
* @param *data: 要计算的数据的数组
* @param data_len: 数据大小
* @return void.
*/
void protocol_data_recv(uint8_t *data, uint16_t data_len)
{
recvbuf_put_data(parser.recv_ptr, PROT_FRAME_LEN_RECV, parser.w_oft, data, data_len);
parser.w_oft = (parser.w_oft + data_len) % PROT_FRAME_LEN_RECV;
}
/**
* @brief 初始化协议(放在main中,初始化)
* @param void
* @return 初始化结果
*/
int32_t protocol_init(void)
{
memset(&parser, 0, sizeof(struct prot_frame_parser_t));
parser.recv_ptr = recv_buf;
return 0;
}
/**
* @brief 接收数据处理(放在主循环中)
* @param void
* @return -1 没有找到正确指令
*/
int8_t receiving_process(void)
{
uint8_t frame_data[128]; // 要能放下最长的帧
uint16_t frame_len = 0; // 帧长度
uint8_t cmd_type = CMD_NONE; // 命令类型
while(1)
{
cmd_type = protocol_frame_parse(frame_data, &frame_len);
switch (cmd_type)
{
case CMD_NONE:
{
return -1;
}
case SET_P_I_D_CMD: //对应上位机“发送PID”按钮,该按钮按下后,执行case中程序
{
uint32_t temp0 = COMPOUND_32BIT(&frame_data[13]);
uint32_t temp1 = COMPOUND_32BIT(&frame_data[17]);
uint32_t temp2 = COMPOUND_32BIT(&frame_data[21]);
float p_temp, i_temp, d_temp;
p_temp = *(float *)&temp0;
i_temp = *(float *)&temp1;
d_temp = *(float *)&temp2;
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_4); //上位机按钮测试,接收到该按钮按下后,灯闪
}
break;
case SET_TARGET_CMD: //对应上位机“发送目标值”按钮
{
int actual_temp = COMPOUND_32BIT(&frame_data[13]);
set_computer_value(SEND_TARGET_CMD, CURVES_CH1, &actual_temp, 1);
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_4); //上位机按钮测试,接收到该按钮按下后,灯闪
}
break;
case START_CMD: //对应上位机“启动”按钮
{
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_4); //上位机按钮测试,接收到该按钮按下后,灯闪
}
break;
case STOP_CMD: //对应上位机”停止“按钮
{
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_4); //上位机按钮测试,接收到该按钮按下后,灯闪
}
break;
case RESET_CMD: //对应上位机”复位“按钮
{
HAL_NVIC_SystemReset();
}
break;
case SET_PERIOD_CMD: //对应上位机”发送周期“按钮
{
uint32_t temp = COMPOUND_32BIT(&frame_data[13]);
}
break;
default:
return -1;
}
}
}
/**
* @brief 设置上位机的值(目标值,发一次,实际值循环发)
* @param cmd :命令
* @param ch: 曲线通道
* @param data£ :参数指针
* @param num£ :参数个数
* @retval 无
*/
void set_computer_value(uint8_t cmd, uint8_t ch, void *data, uint8_t num)
{
uint8_t sum = 0;
num *= 4;
static packet_head_t set_packet;
set_packet.head = FRAME_HEADER;
set_packet.len = 0x0B + num;
set_packet.ch = ch;
set_packet.cmd = cmd;
sum = check_sum(0, (uint8_t *)&set_packet, sizeof(set_packet));
sum = check_sum(sum, (uint8_t *)data, num);
HAL_UART_Transmit(&huart4, (uint8_t *)&set_packet, sizeof(set_packet), 0xFFFFF); //***自行修改
HAL_UART_Transmit(&huart4, (uint8_t *)data, num, 0xFFFFF); //***自行修改
HAL_UART_Transmit(&huart4, (uint8_t *)&sum, sizeof(sum), 0xFFFFF); //***自行修改
}
protocol.h
#ifndef __PROTOCOL_H__
#define __PROTOCOL_H__
#include "main.h"
#ifdef _cplusplus
extern "C" {
#endif
/* 数据接收缓冲区大小 */
#define PROT_FRAME_LEN_RECV 128
/* 校验数据的长度 */
#define PROT_FRAME_LEN_CHECKSUM 1
/* 数据头结构体 */
typedef __packed struct
{
uint32_t head; // 包头
uint8_t ch; // 通道
uint32_t len; // 包长度
uint8_t cmd; // 命令
// uint8_t sum; // 校验和
}packet_head_t;
#define FRAME_HEADER 0x59485A53 // 帧头
/* 通道宏定义 */
#define CURVES_CH1 0x01
#define CURVES_CH2 0x02
#define CURVES_CH3 0x03
#define CURVES_CH4 0x04
#define CURVES_CH5 0x05
/* 指令(下位机 -> 上位机) */
#define SEND_TARGET_CMD 0x01 // 发送上位机通道的目标值
#define SEND_FACT_CMD 0x02 // 发送通道实际值
#define SEND_P_I_D_CMD 0x03 // 发送 PID 值(同步上位机显示的值)
#define SEND_START_CMD 0x04 // 发送启动指令(同步上位机按钮状态)
#define SEND_STOP_CMD 0x05 // 发送停止指令(同步上位机按钮状态)
#define SEND_PERIOD_CMD 0x06 // 发送周期(同步上位机显示的值)
/* 指令(上位机 -> 下位机) */
#define SET_P_I_D_CMD 0x10 // 设置 PID 值
#define SET_TARGET_CMD 0x11 // 设置目标值
#define START_CMD 0x12 // 启动指令
#define STOP_CMD 0x13 // 停止指令
#define RESET_CMD 0x14 // 复位指令
#define SET_PERIOD_CMD 0x15 // 设置周期
/* 空指令 */
#define CMD_NONE 0xFF // 空指令
/* 索引值宏定义 */
#define HEAD_INDEX_VAL 0x3u // 包头索引值(4字节)
#define CHX_INDEX_VAL 0x4u // 通道索引值(1字节)
#define LEN_INDEX_VAL 0x5u // 包长索引值(4字节)
#define CMD_INDEX_VAL 0x9u // 命令索引值(1字节)
#define EXCHANGE_H_L_BIT(data) ((((data) << 24) & 0xFF000000) |\
(((data) << 8) & 0x00FF0000) |\
(((data) >> 8) & 0x0000FF00) |\
(((data) >> 24) & 0x000000FF)) // 交换高低字节
#define COMPOUND_32BIT(data) (((*(data-0) << 24) & 0xFF000000) |\
((*(data-1) << 16) & 0x00FF0000) |\
((*(data-2) << 8) & 0x0000FF00) |\
((*(data-3) << 0) & 0x000000FF)) // 合成为一个字
/**
* @brief 接收数据处理
* @param *data: 要计算的数据的数组.
* @param data_len: 数据的大小
* @return void.
*/
void protocol_data_recv(uint8_t *data, uint16_t data_len);
/**
* @brief 初始化接收协议
* @param void
* @return 初始化结果.
*/
int32_t protocol_init(void);
/**
* @brief 接收的数据处理
* @param void
* @return -1:没有找到一个正确的命令.
*/
int8_t receiving_process(void);
/**
* @brief 设置上位机的值
* @param cmd:命令
* @param ch: 曲线通道
* @param data:参数指针
* @param num:参数个数
* @retval 无
*/
void set_computer_value(uint8_t cmd, uint8_t ch, void *data, uint8_t num);
#ifdef _cplusplus
}
#endif
#endif
main.c
#include "main.h"
void SystemClock_Config(void);
unsigned char target_value[1] = {10};
unsigned char actual_value[1];
int main(void)
{
HAL_Init();
SystemClock_Config();
led_init();
debug_uart4_init(); //串口初始化
protocol_init(); //接收数据初始化
set_computer_value(SEND_TARGET_CMD, CURVES_CH1, target_value, 1); //发送目标值
while (1)
{
receiving_process(); //循环接收并处理数据
set_computer_value(SEND_FACT_CMD, CURVES_CH1, actual_value, 1); //发送实际值
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
}
二、重点分析
protocol.c中的重点函数为:
int8_t receiving_process(void)
作用:接收上位机指令,并解析数据,
其中宏定义与上位机按钮 对应关系:
#define SET_P_I_D_CMD 0x10 // 设置 PID 值
#define SET_TARGET_CMD 0x11 // 设置目标值
#define START_CMD 0x12 // 启动指令
#define STOP_CMD 0x13 // 停止指令
#define RESET_CMD 0x14 // 复位指令
#define SET_PERIOD_CMD 0x15 // 设置周期
接收到对应按钮的指令,执行其宏定义下case中的内容。
*void set_computer_value(uint8_t cmd, uint8_t ch, void data, uint8_t num)
作用:向上位机发送指令
其中宏定义与上位机数据 对应关系:
#define SEND_TARGET_CMD 0x01 // 发送上位机通道的目标值
#define SEND_FACT_CMD 0x02 // 发送通道实际值
#define SEND_P_I_D_CMD 0x03 // 发送 PID 值(同步上位机显示的值)
#define SEND_START_CMD 0x04 // 发送启动指令(同步上位机按钮状态)
#define SEND_STOP_CMD 0x05 // 发送停止指令(同步上位机按钮状态)
#define SEND_PERIOD_CMD 0x06 // 发送周期(同步上位机显示的值)
例子1:
向通道1,发送目标值:target_value
set_computer_value(SEND_TARGET_CMD, CURVES_CH1, target_value, 1);
执行一次发送,即可刷新文本框中内容 和 曲线
例子2:
向通道1,发送实际值:actual_value
set_computer_value(SEND_FACT_CMD, CURVES_CH1, actual_value, 1);
主循环中,循环发送,可实时显示实际值 和 实际值曲线
整体图片
版权声明:本文标题:3-野火PID调试助手程序移植 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dianzi/1727404666a1113235.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论