3-野火PID调试助手程序移植

一、移植

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);     

主循环中,循环发送,可实时显示实际值 和 实际值曲线
在这里插入图片描述
整体图片
在这里插入图片描述

  • 9
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
野火PID调试助手是一个多功能调试工具,可以用于串口调试、网络调试、摄像头调试PID调试等功能。你可以从野火电子论坛上下载这个调试助手,并按照说明进行安装和配置。\[2\] 在使用野火PID调试助手之前,你需要先下载野火关于电机的相关例程,并将其中的通信协议移植到你自己的程序中。这些例程通常使用HAL库编写,所以如果你的电机程序也是使用HAL库编写的,移植通信协议会比较简单。如果你的电机程序是使用标准库编写的,那么将通信协议改成标准库可能会更加麻烦。\[3\] 一旦你将通信协议移植到你的程序中,你就可以在野火PID调试助手中使用PID调试功能了。具体的使用方法可以参考野火电子论坛上的功能说明。\[2\]你可以通过该调试助手调试和优化你的PID算法,以达到更好的控制效果。 #### 引用[.reference_title] - *1* *3* [野火PID上位机通信移植](https://blog.csdn.net/qq_52684874/article/details/119706975)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [野火调试助手使用经验](https://blog.csdn.net/qq_33795842/article/details/122190894)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值