Follow the reference to port the bootloader

This commit is contained in:
2025-09-28 01:14:06 +08:00
parent 422c27846f
commit bd541d585e
23 changed files with 712 additions and 1638 deletions

View File

@@ -26,11 +26,9 @@ set(TARGET_SRC
# Add new source files here
Src/uart.c
Src/led.c
Src/uart_ring_buffer.c
Src/command.c
Src/i2c.c
Src/board_config.c
Src/gpio.c
Src/bootloader.c
)
# 设置输出目录

View File

@@ -1,14 +1,7 @@
#ifndef BOARD_CONFIG_H
#define BOARD_CONFIG_H
#define GD32E23XF4 0x10
#define GD32E23XF6 0x20
#define GD32E23XF8 0x40
/* >>>>>>>>>>>>>>>>>>>>[RS485 PHY DEFINE]<<<<<<<<<<<<<<<<<<<< */
// #define RS485_MAX13487 // RS485 PHY : MAX13487 (AutoDir)
#undef RS485_MAX13487 // RS485 PHY : SP3487 (no AutoDir)
#include <stdint.h>
/* >>>>>>>>>>>>>>>>>>>>[IIC TYPE DEFINE]<<<<<<<<<<<<<<<<<<<< */
@@ -25,22 +18,11 @@
// #define EDDY_DRIVE_CURRENT_DETECTION // Eddy Drive Current Detection : Enable
#undef EDDY_DRIVE_CURRENT_DETECTION // Eddy Drive Current Detection : Disable
/******************************************************************************/
/* >>>>>>>>>>>>>>>>>>>[BOARD TYPE CONFIG]<<<<<<<<<<<<<<<<<<<< */
/* Dynamic USART Configuration Structure */
typedef struct {
uint32_t rcu_usart;
uint32_t usart_periph;
IRQn_Type irq_type;
void (*irq_handler)(void); // 函数指针:指向中断处理函数
} usart_config_t;
#define ULTRASONIC_BOARD
// #define EDDY_BOARD
extern usart_config_t g_usart_config;
extern uint8_t g_mcu_flash_size;
/* USART中断处理函数声明 */
void usart0_irq_handler(void);
void usart1_irq_handler(void);
/******************************************************************************/
@@ -56,15 +38,25 @@ void usart1_irq_handler(void);
/******************************************************************************/
#ifdef ULTRASONIC_BOARD
#define LED_RCU RCU_GPIOA
#define LED_PORT GPIOA
#define LED_PIN GPIO_PIN_9
#elif EDDY_BOARD
#define LED_RCU RCU_GPIOA
#define LED_PORT GPIOA
#define LED_PIN GPIO_PIN_7
#endif
/******************************************************************************/
#define RS485_RCU (g_usart_config.rcu_usart)
#define RS485_PHY (g_usart_config.usart_periph)
#define RS485_IRQ (g_usart_config.irq_type)
/* GD32E230F8 Fixed Configuration */
#define RS485_RCU RCU_USART1
#define RS485_PHY USART1
#define RS485_IRQ USART1_IRQn
#define RS485_GPIO_RCU RCU_GPIOA
#define RS485_GPIO_PORT GPIOA
#define RS485_EN_PIN GPIO_PIN_1
@@ -74,7 +66,4 @@ void usart1_irq_handler(void);
/******************************************************************************/
void mcu_detect_and_config(void);
uint8_t get_flash_size(void);
#endif //BOARD_CONFIG_H

57
Inc/bootloader.h Normal file
View File

@@ -0,0 +1,57 @@
#ifndef BOOTLOADER_H
#define BOOTLOADER_H
#include <stdint.h>
#define FLASH_BASE_ADDRESS 0x08000000
#define FLASH_END_ADDRESS 0x08010000 // GD32E230F8: 64KB Flash
#define APP_FLASH_END_ADDRESS 0x0800EFFF // End of application flash
#define APPLICATION_ADDRESS 0x08002000 // Application starts at 8KB (adjust as needed)
#define FLASH_FLAG_ADDRESS 0x0800FFFC // Address to check if application is present
#define BOOTLOADER_SIZE 0x2000 // 8KB for bootloader
// Ymodem协议常量
#define PACKET_HEADER 3 // 数据包头大小
#define PACKET_1K_SIZE 1024 // 1K数据包大小
#define FILE_NAME_LENGTH 256 // 文件名长度
#define FILE_SIZE_LENGTH 16 // 文件大小字符串长度
// Ymodem控制字符
#define SOH 0x01 // 128字节数据包开始
#define STX 0x02 // 1K字节数据包开始
#define EOT 0x04 // 传输结束
#define ACK 0x06 // 确认
#define NAK 0x15 // 否认
#define CA 0x18 // 取消传输
#define CRC16 0x43 // 'C' - 请求CRC16模式
#define YMODEM_RX_BUFFER_SIZE 1100 // 接收缓冲区大小
#define ISVALIDHEX(c) (((c) >= '0' && (c) <= '9') || \
((c) >= 'A' && (c) <= 'F') || \
((c) >= 'a' && (c) <= 'f'))
#define CONVERTHEX(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \
(((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) : \
((c) - 'a' + 10)))
#define ISVALIDDEC(c) ((c) >= '0' && (c) <= '9')
#define CONVERTDEC(c) ((c) - '0')
extern uint8_t ymodem_rx_buffer[YMODEM_RX_BUFFER_SIZE];
extern uint16_t ymodem_rx_count;
/* Function pointer type for application jump */
typedef void (*pFunction)(void);
/* Function declarations */
uint8_t check_flash_and_jump(uint8_t *buf);
uint32_t str_to_int(uint8_t *input_str, int32_t *int_num);
uint16_t crc16_update(uint16_t crc_in, uint8_t byte);
uint16_t crc16_calculate(const uint8_t* data, uint32_t size);
void ymodem_read_packet_data(void);
int32_t ymodem_receive(uint8_t *buf);
#endif

View File

@@ -1,81 +0,0 @@
/**
* @file command.h
* @brief 串口命令解析与处理模块接口声明。
* @details 提供基于环形缓冲区的串口协议解析、命令处理及状态管理功能,
* 支持格式为 D5 03 LEN [cmd] CRC 的命令帧解析与响应。
*/
#ifndef COMMAND_H
#define COMMAND_H
#include <stdint.h>
#include <stdbool.h>
/**
* @defgroup Command 命令处理模块
* @brief 串口命令解析与处理
* @{
*/
/**
* @section Command_Protocol 协议格式
* 接收命令帧格式:
* @code
* [0] HEADER = 0xD5 // 包头标识
* [1] BOARD_TYPE = 0x03 // 板卡类型标识
* [2] LEN = 数据区字节数 // 有效载荷长度
* [3..(3+LEN-1)] 数据 // 命令数据
* [last] CRC // 校验码从索引1累加到len-2的低8位
* @endcode
*
* 响应帧格式:
* @code
* [0] HEADER = 0xB5 // 响应包头
* [1] TYPE // 响应类型0xF0=成功0xF1..=错误类型)
* [2] LEN // 响应数据长度
* [3..(3+LEN-1)] 数据 // 响应数据
* [last] CRC // 校验码
* @endcode
*
* @section Command_Usage 使用说明
* 1) 初始化环形缓冲区:
* @code{.c}
* uart_ring_buffer_init();
* @endcode
*
* 2) 在主循环中调用命令处理:
* @code{.c}
* while(1) {
* command_process(); // 处理接收到的命令
* // 其他业务逻辑
* }
* @endcode
*
* 3) 查询传感器上报状态:
* @code{.c}
* if(get_sensor_report_enabled()) {
* // 执行传感器数据上报
* }
* @endcode
*/
/**
* @brief 处理串口环形缓冲区中的命令数据。
* @details 基于状态机的非阻塞协议解析器,处理完整的命令帧并自动响应。
* 每次调用处理缓冲区中所有可用数据,遇到错误时自动重置状态机。
* @note 使用静态变量维护解析状态,函数不可重入。
* @warning 依赖环形缓冲区正确实现,建议在主循环中周期调用。
* @ingroup Command
*/
void command_process(void);
/**
* @brief 解析并处理完整的命令帧。
* @param cmd 指向完整命令帧的缓冲区从包头0xD5开始
* @param len 命令帧总长度(字节)。
* @note 内部函数,由 command_process() 调用,一般不直接使用。
* @ingroup Command
*/
void handle_command(const uint8_t *cmd, uint8_t len);
/** @} */ // end of Command group
#endif // COMMAND_H

6
Inc/gpio.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef GPIO_H
#define GPIO_H
void gpio_init(void);
#endif // GPIO_H

127
Inc/i2c.h
View File

@@ -1,127 +0,0 @@
//
// Created by dell on 24-12-20.
//
#ifndef I2C_H
#define I2C_H
#include "gd32e23x_it.h"
#include "gd32e23x.h"
#include "systick.h"
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "board_config.h"
/******************************************************************************/
#define I2C_SPEED 100000U /* 100kHz */
#define I2C_TIME_OUT 5000U /* 5000 loops timeout */
#define I2C_MAX_RETRY 3U /* Maximum retry attempts */
#define I2C_DELAY_10US 10U /* Delay in microseconds for bus reset */
#define I2C_RECOVERY_CLOCKS 9U /* Clock pulses for bus recovery */
#define I2C_MASTER_ADDRESS 0x00U /* Master address (not used) */
/* Legacy compatibility */
#define I2C_OK 1
#define I2C_FAIL 0
#define I2C_END 1
/******************************************************************************/
/* I2C result enumeration */
typedef enum {
I2C_RESULT_SUCCESS = 0, /* Operation successful */
I2C_RESULT_TIMEOUT, /* Timeout occurred */
I2C_RESULT_NACK, /* No acknowledge received */
I2C_RESULT_BUS_BUSY, /* Bus is busy */
I2C_RESULT_ERROR, /* General error */
I2C_RESULT_INVALID_PARAM, /* Invalid parameter */
I2C_RECOVERY_OK,
I2C_RECOVERY_SDA_STUCK_LOW,
I2C_RECOVERY_SCL_STUCK_LOW
} i2c_result_t;
/* I2C state machine enumeration */
typedef enum {
I2C_STATE_IDLE = 0, /* Idle state */
I2C_STATE_START, /* Generate start condition */
I2C_STATE_SEND_ADDRESS, /* Send slave address */
I2C_STATE_CLEAR_ADDRESS, /* Clear address flag */
I2C_STATE_TRANSMIT_REG, /* Transmit register address */
I2C_STATE_TRANSMIT_DATA, /* Transmit data */
I2C_STATE_RESTART, /* Generate restart condition */
I2C_STATE_RECEIVE_DATA, /* Receive data */
I2C_STATE_STOP, /* Generate stop condition */
I2C_STATE_ERROR, /* Error state */
I2C_STATE_END
} i2c_state_t;
/******************************************************************************/
/* Function declarations */
/*!
\brief configure the I2C interface
\param[in] none
\param[out] none
\retval i2c_result_t
*/
i2c_result_t i2c_config(void);
/*!
\brief reset I2C bus with proper recovery
\param[in] none
\param[out] none
\retval i2c_result_t
*/
i2c_result_t i2c_bus_reset(void);
/*!
\brief scan I2C bus for devices
\param[in] none
\param[out] none
\retval none
*/
void i2c_scan(void);
/*!
\brief write 16-bit data to I2C device
\param[in] slave_addr: 7-bit slave address
\param[in] reg_addr: register address
\param[in] data: pointer to 2-byte data array
\param[out] none
\retval i2c_result_t
*/
i2c_result_t i2c_write_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data[2]);
/*!
\brief read 16-bit data from I2C device
\param[in] slave_addr: 7-bit slave address
\param[in] reg_addr: register address
\param[out] data: pointer to 2-byte data buffer
\retval i2c_result_t
*/
i2c_result_t i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data);
/*!
\brief read 16-bit data from I2C device
\param[in] slave_addr: 7-bit slave address
\param[in] reg_addr: register address
\param[out] data: pointer to 2-byte data buffer
\retval i2c_result_t
*/
i2c_result_t i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data);
/*!
\brief get status string for debugging
\param[in] status: i2c_result_t value
\param[out] none
\retval const char* status string
*/
const char* i2c_get_status_string(i2c_result_t status);
#endif //I2C_H

View File

@@ -1,13 +0,0 @@
#ifndef LED_H
#define LED_H
#include "gd32e23x.h"
#include "board_config.h"
void led_init(void);
void led_on(void);
void led_off(void);
void led_toggle(void);
void led_heart_beat(void);
#endif // LED_H

View File

@@ -5,4 +5,8 @@
void rs485_init(void);
uint32_t rs485_send_byte(uint8_t data);
uint32_t rs485_send_str(uint8_t* str, uint16_t len);
#endif // UART_H

View File

@@ -10,12 +10,14 @@
#include <stdint.h>
#include <stdbool.h>
typedef struct uart_ring_buffer uart_ring_buffer_t;
/**
* @def UART_RX_BUFFER_SIZE
* @brief 接收环形缓冲区容量(单位:字节)。
* @note 采用“预留一格”区分空/满策略,最大可用容量为 UART_RX_BUFFER_SIZE-1。
*/
#define UART_RX_BUFFER_SIZE 64
#define UART_RX_BUFFER_SIZE 1100
/**
* @defgroup RingBuffer 环形缓冲区
@@ -101,19 +103,11 @@ bool uart_ring_buffer_put(uint8_t data);
/**
* @brief 清空环形缓冲区。
* @details 复位读/写索引与丢弃计数,相当于逻辑上丢弃所有已接收数据,不擦除数据区内容
* @details 复位读/写索引,相当于逻辑上丢弃所有已接收数据。
* @ingroup RingBuffer
*/
void uart_ring_buffer_clear(void);
/**
* @brief 获取因缓冲区满而被丢弃的字节累计数量。
* @details 该计数在 init/clear 时清零。
* @return 丢弃的累计字节数。
* @ingroup RingBuffer
*/
uint32_t uart_ring_buffer_drop_count(void);
/** @} */
#endif // UART_RING_BUFFER_H

View File

@@ -13,7 +13,7 @@ _Min_Stack_Size = 0x400; /* required amount of stack */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 16K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 4K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 8K
}
/* Sections */

View File

@@ -1,25 +1,25 @@
project(GD32E23x_standard_peripheral LANGUAGES C CXX ASM)
add_library(GD32E23x_standard_peripheral OBJECT
${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_adc.c
${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_cmp.c
${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_crc.c
${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_dbg.c
${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_dma.c
${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_exti.c
# ${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_adc.c
# ${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_cmp.c
# ${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_crc.c
# ${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_dbg.c
# ${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_dma.c
# ${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_exti.c
${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_fmc.c
${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_fwdgt.c
# ${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_fwdgt.c
${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_gpio.c
${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_i2c.c
${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_misc.c
${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_pmu.c
# ${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_pmu.c
${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_rcu.c
${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_rtc.c
${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_spi.c
${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_syscfg.c
${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_timer.c
# ${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_rtc.c
# ${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_spi.c
# ${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_syscfg.c
# ${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_timer.c
${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_usart.c
${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_wwdgt.c
# ${CMAKE_SOURCE_DIR}/SDK/GD32E23x_standard_peripheral/Source/gd32e23x_wwdgt.c
)
target_include_directories(GD32E23x_standard_peripheral PUBLIC

View File

@@ -1,52 +0,0 @@
#include "gd32e23x.h"
#include "board_config.h"
#include "systick.h"
/******************************************************************************/
#define FLASH_SIZE_ADDR (*(const uint8_t *)0x1FFFF7E0) // Flash base address
/******************************************************************************/
/* 前向声明中断处理函数 */
void usart0_irq_handler(void);
void usart1_irq_handler(void);
usart_config_t g_usart_config = {
.rcu_usart = RCU_USART1,
.usart_periph = USART1,
.irq_type = USART1_IRQn,
.irq_handler = usart1_irq_handler // 初始化函数指针
};
uint8_t g_mcu_flash_size = 0;
void mcu_detect_and_config(void) {
g_mcu_flash_size = FLASH_SIZE_ADDR;
switch (g_mcu_flash_size) {
case GD32E23XF4:
g_usart_config.rcu_usart = RCU_USART0;
g_usart_config.usart_periph = USART0;
g_usart_config.irq_type = USART0_IRQn;
g_usart_config.irq_handler = usart0_irq_handler; // 指向USART0处理函数
break;
case GD32E23XF6:
g_usart_config.rcu_usart = RCU_USART1;
g_usart_config.usart_periph = USART1;
g_usart_config.irq_type = USART1_IRQn;
g_usart_config.irq_handler = usart1_irq_handler; // 指向USART1处理函数
break;
default: // Default to GD32E23XF8
g_usart_config.rcu_usart = RCU_USART1;
g_usart_config.usart_periph = USART1;
g_usart_config.irq_type = USART1_IRQn;
g_usart_config.irq_handler = usart1_irq_handler; // 指向USART1处理函数
break;
}
}
uint8_t get_flash_size(void) {
return g_mcu_flash_size;
}

476
Src/bootloader.c Normal file
View File

@@ -0,0 +1,476 @@
#include "bootloader.h"
#include "board_config.h"
#include "gd32e23x.h"
#include "uart_ring_buffer.h"
#include "systick.h"
uint8_t ymodem_rx_buffer[YMODEM_RX_BUFFER_SIZE] = {0};
uint16_t ymodem_rx_count = 0;
static uint8_t temp_buffer[PACKET_1K_SIZE]; // 临时缓冲区,用于接收数据包
/**
* @brief 擦除Flash页面适配同事的flash_earse函数
* @param page_address 要擦除的页面地址
* @return 擦除结果状态
*/
uint8_t flash_erase_page(uint32_t page_address)
{
fmc_state_enum fmc_state = FMC_READY;
// 清除Flash状态标志
fmc_flag_clear(FMC_FLAG_BUSY | FMC_FLAG_PGERR | FMC_FLAG_PGAERR | FMC_FLAG_WPERR | FMC_FLAG_END);
// 解锁Flash
fmc_unlock();
// 执行页面擦除
fmc_state = fmc_page_erase(page_address);
// 返回结果0=成功1=失败)
return (fmc_state == FMC_READY) ? 0 : 1;
}
/**
* @brief 写入数据到Flash适配同事的write_data函数
* @param flash_address Flash地址指针会被更新
* @param data 要写入的数据指针
* @param data_length 数据长度以32位字为单位
* @return 写入结果0=成功1=写入错误2=验证错误
*/
uint8_t flash_write_data(__IO uint32_t* flash_address, uint32_t* data, uint32_t data_length)
{
uint32_t i = 0;
// 清除Flash状态标志可选根据需要启用
// fmc_flag_clear(FMC_FLAG_PGERR | FMC_FLAG_PGAERR | FMC_FLAG_WPERR | FMC_FLAG_END);
// 逐字写入数据
for (i = 0; (i < data_length) && (*flash_address <= (APP_FLASH_END_ADDRESS - 4)); i++) {
// 写入32位数据到Flash
if (fmc_word_program(*flash_address, *(uint32_t*)(data + i)) == FMC_READY) {
// 验证写入的数据
if (*(uint32_t*)*flash_address != *(uint32_t*)(data + i)) {
// Flash内容与写入数据不匹配
return 2;
}
// 增加Flash地址指针下一个32位字
*flash_address += 4;
} else {
// Flash写入过程中发生错误
return 1;
}
}
// 写入成功
return 0;
}
/**
* @brief Check flash flag and jump to application if valid
* @param buf Pointer to flash memory location to check
* @return uint8_t 1=stay in bootloader, 0=jumped to application (won't return)
*/
uint8_t check_flash_and_jump(uint8_t *buf)
{
uint32_t jump_address;
pFunction jump_to_application;
if (*buf == 0xFF) {
// Flash为空继续bootloader模式
return 1;
}
fmc_lock(); // 锁定FMC以防止意外修改
// 读取应用程序的栈指针和复位向量
jump_address = *(__IO uint32_t*)(APPLICATION_ADDRESS + 4);
// 验证栈指针是否有效应该在SRAM范围内
// if ((app_stack_pointer & 0xFFF00000) != 0x20000000) {
// // 栈指针无效继续bootloader模式
// return 1;
// }
// 验证跳转地址是否有效应该在Flash应用程序区域内且为奇数-Thumb模式
// if ((jump_address < APPLICATION_ADDRESS) ||
// (jump_address > 0x08010000) || // 假设Flash大小为64KB
// ((jump_address & 0x01) == 0)) { // Thumb模式地址应为奇数
// // 跳转地址无效继续bootloader模式
// return 1;
// }
/* Ensure flash operations are complete */
/* Get application reset vector */
jump_address = *(__IO uint32_t*)(APPLICATION_ADDRESS + 4);
/* Basic validity check - reset vector should be in flash and odd (Thumb mode) */
// if ((jump_address < APPLICATION_ADDRESS) || ((jump_address & 0x01) == 0)) {
// /* Invalid jump address, stay in bootloader */
// return 1;
// }
/* Create function pointer */
jump_to_application = (pFunction)jump_address;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*)APPLICATION_ADDRESS);
/* Disable interrupts for safe transition */
__disable_irq();
/* Jump to user application - this function will not return */
jump_to_application();
/* Should never reach here */
return 0; /* Should never reach here */
}
/**
* @brief Convert string to integer (supports hex, decimal, K, M suffixes)
* @param input_str Input string to convert
* @param int_num Pointer to store converted integer
* @return 1 if successful, 0 if failed
*/
uint32_t str_to_int(uint8_t *input_str, int32_t *int_num)
{
uint32_t i = 0, res = 0;
uint32_t val = 0;
if (input_str[0] == '0' && (input_str[1] == 'x' || input_str[1] == 'X')) {
// Hexadecimal input
if (input_str[2] == '\0') {
return 0;
}
for (i = 2; i < 11; i++) {
if (input_str[i] == '\0') {
*int_num = val;
res = 1;
break;
}
if (ISVALIDHEX(input_str[i])) {
val = (val << 4) + CONVERTHEX(input_str[i]);
} else {
res = 0;
break;
}
}
if (i >= 11) {
res = 0; // Over 8 digit hex - invalid
}
} else {
// Decimal input (max 10-digit)
for (i = 0; i < 11; i++) {
if (input_str[i] == '\0') {
*int_num = val;
res = 1;
break;
} else if ((input_str[i] == 'k' || input_str[i] == 'K') && (i > 0)) {
val = val << 10; // Multiply by 1024
*int_num = val;
res = 1;
break;
} else if ((input_str[i] == 'm' || input_str[i] == 'M') && (i > 0)) {
val = val << 20; // Multiply by 1024*1024
*int_num = val;
res = 1;
break;
} else if (ISVALIDDEC(input_str[i])) {
val = val * 10 + CONVERTDEC(input_str[i]);
} else {
res = 0;
break;
}
}
if (i >= 11) {
res = 0; // Over 10 digit decimal - invalid
}
}
return res;
}
/**
* @brief Update CRC16 value with a single byte
* @param crc_in Current CRC value
* @param byte Byte to process
* @return Updated CRC value
*/
uint16_t crc16_update(uint16_t crc_in, uint8_t byte)
{
uint32_t crc = crc_in;
uint32_t in = byte | 0x100;
do {
crc <<= 1;
in <<= 1;
if (in & 0x100) {
++crc;
}
if (crc & 0x10000) {
crc ^= 0x1021;
}
} while (!(in & 0x10000));
return crc & 0xFFFFU;
}
/**
* @brief Calculate CRC16 for data buffer
* @param data Pointer to data buffer
* @param size Size of data in bytes
* @return Calculated CRC16 value
*/
uint16_t crc16_calculate(const uint8_t* data, uint32_t size)
{
uint32_t crc = 0;
const uint8_t* data_end = data + size;
while (data < data_end) {
crc = crc16_update(crc, *data++);
}
crc = crc16_update(crc, 0);
crc = crc16_update(crc, 0);
return crc & 0xFFFFU;
}
/**
* @brief 接收Ymodem数据包数据
* 等待并读取完整的数据包到缓冲区中
* 该函数保持与原始实现相同的行为逻辑
*/
void ymodem_read_packet_data(void)
{
// 重置接收计数器
ymodem_rx_count = 0;
// 等待数据到达(阻塞等待)
while (uart_ring_buffer_available() <= 0) {
// 可选:发送确认字节(当前被禁用)
// ymodem_send_response(YMODEM_ACK);
delay_ms(1000); // 等待1秒后重新检查
}
// 等待数据传输完成,确保接收到完整数据包
delay_ms(200);
// 读取环形缓冲区中的所有可用数据
while (uart_ring_buffer_available() > 0) {
int received_byte = uart_ring_buffer_get();
if (received_byte >= 0) {
ymodem_rx_buffer[ymodem_rx_count++] = (uint8_t)received_byte;
}
}
}
/**
* @brief Ymodem接收函数直接移植同事的逻辑
* @param buf 未使用(保持接口兼容)
* @return 接收的文件大小,负数表示错误
*/
int32_t ymodem_receive(uint8_t *buf)
{
uint8_t file_size[FILE_SIZE_LENGTH], *file_ptr;
int32_t i, j = 0, packet_length, session_done, file_done, packets_received, errors, session_begin, size = 0, isize = 0, singledata = 0;
uint32_t flash_destination, ram_source;
uint8_t temp = 0, eot_flag = 0, crc_high = 0, crc_low = 0;
uint16_t crc_value = 0;
uint32_t erase_page = APPLICATION_ADDRESS; // 从应用程序地址开始擦除
uint8_t pack = 1;
// 解锁Flash并擦除应用程序区域
fmc_unlock();
flash_destination = APPLICATION_ADDRESS;
// 擦除应用程序区域从APPLICATION_ADDRESS到FLASH_END_ADDRESS
for (; erase_page < FLASH_END_ADDRESS; erase_page += 1024) {
flash_erase_page(erase_page);
}
temp = 0;
// 发送CRC16请求等待数据
while (uart_ring_buffer_available() <= 0) {
rs485_send_byte(CRC16);
delay_ms(1000);
}
delay_ms(200);
// 读取第一个数据包
ymodem_read_packet_data();
// 主循环
for (session_done = 0, errors = 0, session_begin = 0;;) {
for (packets_received = 0, file_done = 0;;) {
delay_ms(2000);
switch (temp) {
case 0: // 处理文件头数据包
if (ymodem_rx_buffer[0] == SOH) { // 128字节数据包
// 验证包序号
if (ymodem_rx_buffer[1] != ((ymodem_rx_buffer[2] ^ 0xFF) & 0xFF))
break;
if (ymodem_rx_buffer[3] != 0) { // 有文件信息
// 提取文件名
for (i = 0, file_ptr = ymodem_rx_buffer + PACKET_HEADER;
(*file_ptr != 0) && (i < FILE_NAME_LENGTH);) {
file_ptr++;
}
// 提取文件大小
for (i = 0, file_ptr++; (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH);) {
file_size[i++] = *file_ptr++;
}
file_size[i++] = '\0';
// 转换文件大小
str_to_int(file_size, &size);
// 计算需要的1K数据包数量
isize = size / PACKET_1K_SIZE;
singledata = size - (isize * PACKET_1K_SIZE);
// 验证CRC
crc_value = crc16_calculate(&ymodem_rx_buffer[3], 128);
crc_low = crc_value & 0xFF;
crc_value >>= 8;
crc_high = crc_value & 0xFF;
if (crc_high != ymodem_rx_buffer[131] || crc_low != ymodem_rx_buffer[132]) {
ymodem_rx_count = 0;
ymodem_rx_buffer[0] = 0;
rs485_send_byte(NAK);
ymodem_read_packet_data();
break;
}
temp = 1;
ymodem_rx_count = 0;
ymodem_rx_buffer[0] = 0;
rs485_send_byte(ACK);
rs485_send_byte(CRC16);
ymodem_read_packet_data();
}
} else if (ymodem_rx_buffer[0] == CA) { // 取消传输
if (ymodem_rx_buffer[1] == CA && ymodem_rx_buffer[2] == CA &&
ymodem_rx_buffer[3] == CA && ymodem_rx_buffer[4] == CA) {
ymodem_rx_count = 0;
ymodem_rx_buffer[0] = 0;
rs485_send_byte(ACK);
temp = 0;
}
ymodem_rx_count = 0;
return -3;
}
break;
case 1: // 处理数据包
if (ymodem_rx_buffer[0] == STX) { // 1K字节数据包
// 验证包序号
if (ymodem_rx_buffer[1] != ((ymodem_rx_buffer[2] ^ 0xFF) & 0xFF) &&
ymodem_rx_buffer[1] == pack) {
ymodem_rx_count = 0;
ymodem_rx_buffer[0] = 0;
rs485_send_byte(NAK);
break;
}
// 验证CRC
crc_value = crc16_calculate(&ymodem_rx_buffer[3], PACKET_1K_SIZE);
crc_low = crc_value & 0xFF;
crc_value >>= 8;
crc_high = crc_value & 0xFF;
if (crc_high != ymodem_rx_buffer[PACKET_1K_SIZE + 3] ||
crc_low != ymodem_rx_buffer[PACKET_1K_SIZE + 4]) {
ymodem_rx_count = 0;
ymodem_rx_buffer[0] = 0;
rs485_send_byte(NAK);
break;
}
// 处理单独数据最后一包可能不满1K
if (singledata != 0) {
// 预留处理不完整数据包的逻辑
}
// 复制数据到临时缓冲区
memcpy(temp_buffer, ymodem_rx_buffer + PACKET_HEADER, PACKET_1K_SIZE);
ram_source = (uint32_t)temp_buffer;
fmc_unlock();
// 写入Flash
if (flash_write_data(&flash_destination, (uint32_t*)ram_source, PACKET_1K_SIZE) == 0) {
isize--;
ymodem_rx_count = 0;
ymodem_rx_buffer[0] = 0;
pack++;
rs485_send_byte(ACK);
// 如果是最后一包,写入应用程序标志
if (isize == 1) {
fmc_unlock();
// flash_write_app_flag();
fmc_word_program(FLASH_FLAG_ADDRESS, 0xEEEE);
}
ymodem_read_packet_data();
} else {
rs485_send_byte(CA);
rs485_send_byte(CA);
temp = 0;
ymodem_rx_count = 0;
return -2;
}
} else if (ymodem_rx_buffer[0] == EOT) { // 传输结束
if (eot_flag == 1) {
eot_flag = 0;
ymodem_rx_count = 0;
ymodem_rx_buffer[0] = 0;
rs485_send_byte(ACK);
rs485_send_byte(CRC16);
ymodem_read_packet_data();
temp = 3;
}
ymodem_rx_count = 0;
ymodem_rx_buffer[0] = 0;
rs485_send_byte(ACK);
temp = 3;
} else if (ymodem_rx_buffer[0] == CA) { // 取消传输
if (ymodem_rx_buffer[1] == CA && ymodem_rx_buffer[2] == CA &&
ymodem_rx_buffer[3] == CA && ymodem_rx_buffer[4] == CA) {
ymodem_rx_count = 0;
ymodem_rx_buffer[0] = 0;
rs485_send_byte(ACK);
temp = 0;
}
ymodem_rx_count = 0;
return -3;
}
ymodem_rx_count = 0;
break;
case 3: // 传输完成
temp = 0;
fmc_lock();
ymodem_rx_count = 0;
ymodem_rx_buffer[0] = 0;
rs485_send_byte(ACK);
ymodem_rx_count = 0;
return size;
}
}
}
}

View File

@@ -1,403 +0,0 @@
/**
* @file command.c
* @brief 串口命令解析与处理模块实现
* @details 实现基于状态机的协议解析器,支持 D5 03 LEN [cmd] CRC 格式的命令处理,
* 包含命令帧解析、响应生成和传感器状态管理功能。
* @author Hulk
* @date 2025-08-13
* @version 1.0.0
* @ingroup Command
*/
#include "command.h"
#include "uart_ring_buffer.h"
#include "led.h"
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "board_config.h"
#include "gd32e23x_usart.h"
/* ============================================================================
* 协议格式说明
* ============================================================================ */
/**
* @name 协议帧格式
* @{
* @details
* Host -> Device 命令帧格式:
* [0] HEADER = 0xD5 // 包头标识
* [1] BOARD_TYPE = 0x03 // 板卡类型标识
* [2] LEN = 数据区字节数 // 有效载荷长度
* [3..(3+LEN-1)] 数据 // 命令数据,如 "M1", "M2S123"
* [last] CRC = 校验码 // 从索引1到(last-1)的累加和低8位
*
* 最小协议包长度为 6 字节
* 数据示例(两字节命令):"M1" / "M2" / "M3"
*
* Device -> Host 响应帧格式:
* [0] 0xB5 // 响应包头
* [1] TYPE // 响应类型0xF0=成功0xF1..=错误类型)
* [2] LEN // 响应数据长度
* [3..(3+LEN-1)] 数据 // 响应数据,如 "ok", "err"
* [last] CRC // 校验码(同命令帧规则)
* @}
*/
/* ============================================================================
* 协议常量定义
* ============================================================================ */
/** @name 协议帧标识符
* @{ */
#define PROTOCOL_PACKAGE_HEADER 0xD5 /**< 命令帧包头标识 */
#define PROTOCOL_BOARD_TYPE 0x03 /**< 板卡类型标识 */
/** @} */
/** @name 命令长度限制
* @{ */
#define COMMAND_MIN_LEN 2 /**< 最小命令长度,如"M1" */
#define PROTOCOL_MIN_FRAME_LEN (3 + COMMAND_MIN_LEN + 1) /**< 最小完整帧长度header+type+len+payload+crc = 6 */
#define PROTOCOL_MAX_FRAME_LEN 16 /**< 最大完整帧长度 */
/** @} */
/** @name 响应帧标识符
* @{ */
#define RESP_HEADER 0xB5 /**< 响应帧包头标识 */
#define RESP_TYPE_OK 0xF0 /**< 成功响应类型 */
#define RESP_TYPE_CRC_ERR 0xF1 /**< CRC校验错误 */
#define RESP_TYPE_HEADER_ERR 0xF2 /**< 包头错误 */
#define RESP_TYPE_TYPE_ERR 0xF3 /**< 类型错误 */
#define RESP_TYPE_LEN_ERR 0xF4 /**< 长度错误 */
/** @} */
/* ============================================================================
* 模块内部变量
* ============================================================================ */
/** @name 预设响应数据
* @{ */
static const uint8_t s_report_status_ok[] = { 'o', 'k' }; /**< 成功响应数据 */
static const uint8_t s_report_status_err[] = { 'e','r','r' }; /**< 错误响应数据 */
/** @} */
/* ============================================================================
* 公共接口函数
* ============================================================================ */
/**
* @brief 计算协议包的 8 位累加校验值Checksum
* @details 对输入缓冲区逐字节累加并取低 8 位,累加范围为 data[1] 至 data[len-2]
* 即不包含包头 HEADER索引 0与尾部 CRC 字节(索引 len-1
* 当 len 小于最小协议帧长度PACKAGE_MIN_LENGTH时返回 0。
* @param data 指向待校验的完整协议包缓冲区。
* @param len 缓冲区总长度(字节),应满足 header + type + len + payload + crc 的最小格式。
* @return uint8_t 计算得到的 8 位校验值。
* @note 本函数实现为简单求和校验Checksum非多项式 CRC与本协议“从索引 1 累加到 len-2”的规则一致。
* @ingroup Command
*/
static uint8_t command_sum_crc_calc(const uint8_t *data, uint8_t len)
{
uint16_t crc = 0;
// 仅在满足协议最小帧长时计算header + type + len + payload + crc
if (len < PROTOCOL_MIN_FRAME_LEN) return 0;
// 累加从索引 1 到 len-2 的字节(不含 header 和 crc 字节)
for (uint8_t i = 1; i < (len - 1); i++)
{
crc += data[i];
}
return (uint8_t)(crc & 0xFF);
}
/**
* @brief 发送协议响应帧使用GD32E230标准库
* @details 构造并发送格式为 B5 TYPE LEN [payload] CRC 的响应帧,
* 自动计算CRC校验值并通过串口输出。
* @param type 响应类型码(如 RESP_TYPE_OK, RESP_TYPE_CRC_ERR 等)。
* @param payload 指向响应数据的缓冲区当len为0时可为NULL。
* @param len 响应数据长度字节为0时不复制payload数据。
* @note 内部使用固定大小缓冲区,超长响应将被丢弃。
* @warning 使用GD32E230标准库函数发送确保串口已正确初始化。
* @ingroup Command
*/
static void send_response(uint8_t type, const uint8_t *payload, uint8_t len)
{
uint8_t buf_len = (uint8_t)(3 + len + 1);
uint8_t buf[16]; // 简单场景足够,必要时可增大
if (buf_len > sizeof(buf)) return; // 防御
buf[0] = RESP_HEADER;
buf[1] = type;
buf[2] = len;
// 简化逻辑只有当len > 0且payload非空时才复制数据
if (len > 0 && payload != NULL) {
for (uint8_t i = 0; i < len; i++) {
buf[3 + i] = payload[i];
}
}
buf[buf_len - 1] = command_sum_crc_calc(buf, buf_len);
// 使用GD32E230标准库函数逐字节发送标准库实现
for (uint8_t i = 0; i < buf_len; i++) {
// 等待发送缓冲区空
while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {}
usart_data_transmit(RS485_PHY, buf[i]);
}
// 等待发送完成
while (usart_flag_get(RS485_PHY, USART_FLAG_TC) == RESET) {}
// // 使用printf发送通过重定向到串口
// for (uint8_t i = 0; i < buf_len; i++) {
// printf("%c", buf[i]);
// }
// // 刷新缓冲区
// fflush(stdout);
}
/**
* @brief 判断字符是否为十进制数字字符。
* @param c 待检查的字符ASCII码值
* @return bool 判断结果。
* @retval true 字符为 '0' 到 '9' 之间的数字字符。
* @retval false 字符不是十进制数字字符。
* @ingroup Command
*/
static inline bool is_dec_digit(uint8_t c) { return (c >= '0' && c <= '9'); }
/**
* @brief 从缓冲区解析十进制无符号整数。
* @details 从指定位置开始连续读取十进制数字字符累加构成32位无符号整数。
* 遇到非数字字符或到达长度限制时停止解析。
* @param s 指向待解析字符缓冲区的起始位置。
* @param n 允许解析的最大字符数。
* @param out 输出参数存储解析结果可为NULL。
* @return uint8_t 实际消耗的字符数。
* @retval 0 首字符不是数字,解析失败。
* @retval >0 成功解析的数字字符个数。
* @note 不处理符号、空白字符或进制前缀。
* @warning 不进行溢出检查超出uint32_t范围时按无符号算术溢出处理。
* @ingroup Command
*/
static uint8_t parse_uint_dec(const uint8_t *s, uint8_t n, uint32_t *out)
{
uint8_t i = 0;
uint32_t v = 0;
while (i < n && is_dec_digit(s[i]))
{
v = v * 10u + (uint32_t)(s[i] - '0');
i++;
}
if (i == 0) return 0; // 未读到数字
if (out) *out = v; //
return i;
}
/* ============================================================================
* 命令处理函数
* ============================================================================ */
/**
* @brief 解析并处理完整的命令帧。
* @details 处理经过协议校验的完整命令帧,支持以下命令格式:
* - 无参数命令M<数字>(如 M1、M2、M10、M201
* - 带参数命令M<数字>S<参数>(如 M100S123参数为十进制
*
* 支持的命令:
* - M1: 开启LED启用传感器上报
* - M2: 关闭LED禁用传感器上报
* - M100S<value>: 设置PWM值示例
*
* @param frame 指向完整命令帧的缓冲区从包头0xD5开始
* @param len 命令帧总长度(字节)。
* @note 函数内部进行帧格式校验,格式错误时自动发送错误响应。
* @warning 假设输入帧已通过基本协议校验包头、类型、CRC等
* @ingroup Command
*/
void handle_command(const uint8_t *frame, uint8_t len) {
// 帧格式D5 03 LEN [cmd] CRC; cmd 支持变长,如 "M1"、"M10"、"M201"、"M123S400",有最小长度限制和命令长度校验
uint8_t cmd_len = frame[2];
if (len < PROTOCOL_MIN_FRAME_LEN || (uint8_t)(3 + cmd_len + 1) != len) return; // 长度不匹配或者小于最小限制
const uint8_t *cmd = &frame[3]; // 提取命令部分
// 命令必须以 'M' 开头
if (cmd[0] != 'M'){
send_response(RESP_TYPE_TYPE_ERR, s_report_status_err, sizeof(s_report_status_err));
return;
}
// 从 'M' 后开始解析
uint8_t cmd_index = 1;
// 解析M后的十进制数即命令本体
uint32_t base_cmd = 0;
uint8_t used_base_cmd = parse_uint_dec(&cmd[cmd_index], (cmd_len - cmd_index), &base_cmd);
if (used_base_cmd == 0)
{
// 'M' 后没有数字,格式错误
send_response(RESP_TYPE_LEN_ERR, s_report_status_err, sizeof(s_report_status_err));
return;
}
cmd_index = (uint8_t)(cmd_index + used_base_cmd); // 更新索引到命令后
// 情况A无附加参数的基础命令
if (cmd_index == cmd_len) {
// 仅基础命令,如 M1, M2, M3
switch (base_cmd) {
case 1u: // M1
send_response(RESP_TYPE_OK, s_report_status_ok, sizeof(s_report_status_ok));
return;
// case 2u: // M2
// return;
// case 3u:
// return;
// case 4u:
// return;
// case 201u: // M201命令
// send_response(RESP_TYPE_OK, s_report_status_ok, sizeof(s_report_status_ok));
// return;
default:
// 其它无参数命令在此扩展示例M100处理逻辑该如何待定
// send_response(RESP_TYPE_OK, s_report_status_ok, sizeof(s_report_status_ok));
// return;
break;
}
// 未在处理列表的无参数基础命令,回复错误
send_response(RESP_TYPE_TYPE_ERR, s_report_status_err, sizeof(s_report_status_err));
return;
}
// 情况B有附加参数的命令
if (cmd[cmd_index] == 'S') {
cmd_index++;
uint32_t param_value = 0;
const uint8_t used_param_cmd = parse_uint_dec(&cmd[cmd_index], (uint8_t)(cmd_len - cmd_index), &param_value);
if (used_param_cmd == 0) {
// 'S' 后没有数字,格式错误
send_response(RESP_TYPE_LEN_ERR, s_report_status_err, sizeof(s_report_status_err));
return;
}
switch (base_cmd)
{
// case 100u:
// // set_pwm(param_value);
// printf("Set PWM to %u\n", param_value);
// return;
default:
break;
}
send_response(RESP_TYPE_TYPE_ERR, s_report_status_err, sizeof(s_report_status_err));
}
}
/**
* @brief 处理串口环形缓冲区中的命令数据,解析完整的协议帧。
* @details 本函数实现一个基于状态机的协议解析器,用于处理格式为 D5 03 LEN [cmd] CRC 的命令帧:
* - 状态1等待包头字节 PROTOCOL_PACKAGE_HEADER (0xD5)
* - 状态2接收板卡类型字节 PROTOCOL_BOARD_TYPE (0x03)
* - 状态3接收长度字段并计算期望的完整帧长度
* - 状态4继续接收剩余数据直到完整帧
* - 状态5对完整帧进行校验包头、板卡类型、CRC并处理
*
* 函数采用非阻塞方式处理,每次调用处理缓冲区中所有可用数据。
* 遇到格式错误、长度异常或校验失败时自动重置状态机。
*
* @note 本函数使用静态变量维护解析状态,因此不可重入。在中断环境中使用需注意并发安全。
* 协议帧最大长度受 PROTOCOL_MAX_FRAME_LEN 限制,超出范围的帧将被丢弃。
*
* @warning 函数依赖 uart_ring_buffer_available() 和 uart_ring_buffer_get()
* 正确实现,若这些函数有缺陷可能导致死循环或数据丢失。
*
* @see handle_command() 用于处理校验通过的完整命令帧
* @see command_sum_crc_calc() 用于计算和校验 CRC 值
* @see send_response() 用于发送错误响应
*
* @ingroup Command
*/
void command_process(void) {
static uint8_t cmd_buf[PROTOCOL_MAX_FRAME_LEN];
static uint8_t cmd_len = 0;
static uint8_t expected_cmd_len = 0; // 0 表示尚未确定总长度
while (uart_ring_buffer_available() > 0) {
int byte = uart_ring_buffer_get();
if (byte < 0) break;
if (cmd_len == 0) {
if ((uint8_t)byte == PROTOCOL_PACKAGE_HEADER) {
cmd_buf[cmd_len++] = (uint8_t)byte;
expected_cmd_len = 0; // 等待进一步字段以确定长度
} else {
// 丢弃非起始字节
}
continue;
}
if (cmd_len >= PROTOCOL_MAX_FRAME_LEN) {
// 防御:缓冲溢出,复位状态机
cmd_len = 0;
expected_cmd_len = 0;
}
// 缓存后续字节
cmd_buf[cmd_len++] = (uint8_t)byte;
// 当到达长度字段(索引 2确定总长度3 + LEN + 1
if (cmd_len == 3) {
uint8_t payload_len = cmd_buf[2];
expected_cmd_len = (uint8_t)(3 + payload_len + 1);
if (expected_cmd_len > PROTOCOL_MAX_FRAME_LEN) {
// 异常:长度超界,复位状态机
cmd_len = 0;
expected_cmd_len = 0;
}
continue;
}
if (expected_cmd_len > 0 && cmd_len == expected_cmd_len) {
// 到帧尾,进行各项校验
bool verification_status = true;
#ifdef DEBUG_VERBOSE
if (cmd_buf[0] != PROTOCOL_PACKAGE_HEADER) {
send_response(RESP_TYPE_HEADER_ERR, s_report_status_err, sizeof(s_report_status_err));
verification_status = false;
}
#endif
if (verification_status && cmd_buf[1] != PROTOCOL_BOARD_TYPE) {
send_response(RESP_TYPE_TYPE_ERR, s_report_status_err, sizeof(s_report_status_err));
verification_status = false;
}
if (verification_status) {
uint8_t crc_calc = command_sum_crc_calc(cmd_buf, expected_cmd_len);
uint8_t crc_recv = cmd_buf[expected_cmd_len - 1];
if (crc_calc != crc_recv) {
send_response(RESP_TYPE_CRC_ERR, s_report_status_err, sizeof(s_report_status_err));
verification_status = false;
}
}
if (verification_status) {
handle_command(cmd_buf, expected_cmd_len);
}
// 复位,等待下一帧
cmd_len = 0;
expected_cmd_len = 0;
}
}
}

View File

@@ -36,7 +36,6 @@ OF SUCH DAMAGE.
#include "systick.h"
#include "uart.h"
#include "uart_ring_buffer.h"
#include "led.h"
#include "board_config.h"
/*!
@@ -98,20 +97,20 @@ void PendSV_Handler(void)
\retval none
*/
void SysTick_Handler(void) {
led_heart_beat(); // LED心跳指示灯
delay_decrement();
}
void USART0_IRQHandler(void) {
// 检查当前配置是否使用USART0并且函数指针不为空
if(g_usart_config.usart_periph == USART0 && g_usart_config.irq_handler != 0) {
g_usart_config.irq_handler(); // 通过函数指针调用对应的处理函数
}
void USART1_IRQHandler(void) {
// 处理USART1的接收中断
if(usart_interrupt_flag_get(USART1, USART_INT_FLAG_RBNE)) {
uint8_t data = usart_data_receive(USART1);
// 使用原有的环形缓冲区处理逻辑
(void)uart_ring_buffer_put(data); // 缓冲满时丢弃,返回值可用于统计
}
void USART1_IRQHandler(void) {
// 检查当前配置是否使用USART1并且函数指针不为空
if(g_usart_config.usart_periph == USART1 && g_usart_config.irq_handler != 0) {
g_usart_config.irq_handler(); // 通过函数指针调用对应的处理函数
// 处理USART1的空闲中断
if(usart_interrupt_flag_get(USART1, USART_INT_FLAG_IDLE)) {
usart_interrupt_flag_clear(USART1, USART_INT_FLAG_IDLE);
// 在这里添加空闲中断处理逻辑
}
}

33
Src/gpio.c Normal file
View File

@@ -0,0 +1,33 @@
#include "gd32e23x.h"
#include "gpio.h"
#include "systick.h"
#include "board_config.h"
/**
* ************************************************************************
* @brief 初始化 GPIO 为确定状态
*
* @details 本函数仅为外设提供确定的初始状态避免因GPIO状态不确定而导致的异常动作。
* 在系统复位后GPIO寄存器可能处于不确定状态通过显式配置确保
* - 未使用的GPIO配置为输入或低功耗状态
* - 关键控制信号配置为安全的默认状态
* - 防止外设因GPIO状态不明确而产生意外行为
*
* @note 此函数应在系统初始化早期调用,在启用具体外设功能之前执行
* @note 具体的外设功能初始化应在各自模块中单独进行
*
* @param none
* @return none
* ************************************************************************
*/
void gpio_init(void) {
// 使能 LED 时钟
rcu_periph_clock_enable(LED_RCU);
// 配置 LED 为输出模式
gpio_mode_set(LED_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_PIN);
gpio_output_options_set(LED_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, LED_PIN);
// 初始化 LED 为低电平LED 开启)
gpio_bit_reset(LED_PORT, LED_PIN);
}

677
Src/i2c.c
View File

@@ -1,677 +0,0 @@
//
// Created by dell on 24-12-20.
//
#include "i2c.h"
/*!
\brief configure the GPIO ports
\param[in] none
\param[out] none
\retval none
*/
void i2c_gpio_config(void) {
/* enable IIC GPIO clock */
rcu_periph_clock_enable(RCU_GPIO_I2C);
/* connect I2C_SCL_PIN to I2C_SCL */
gpio_af_set(I2C_SCL_PORT, I2C_GPIO_AF, I2C_SCL_PIN);
/* connect I2C_SDA_PIN to I2C_SDA */
gpio_af_set(I2C_SDA_PORT, I2C_GPIO_AF, I2C_SDA_PIN);
/* configure GPIO pins of I2C */
gpio_mode_set(I2C_SCL_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, I2C_SCL_PIN);
gpio_output_options_set(I2C_SCL_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SCL_PIN);
gpio_mode_set(I2C_SDA_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, I2C_SDA_PIN);
gpio_output_options_set(I2C_SDA_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SDA_PIN);
}
/*!
\brief configure the I2CX interface
\param[in] none
\param[out] none
\retval none
*/
i2c_result_t i2c_config(void) {
/* configure I2C GPIO */
i2c_gpio_config();
/* enable I2C clock */
rcu_periph_clock_enable(RCU_I2C);
/* configure I2C clock */
i2c_clock_config(I2C0, I2C_SPEED, I2C_DTCY_2);
/* configure I2C address */
i2c_mode_addr_config(I2C0, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0xA0);
/* enable I2CX */
i2c_enable(I2C0);
/* enable acknowledge */
i2c_ack_config(I2C0, I2C_ACK_ENABLE);
return I2C_RESULT_SUCCESS;
}
/* wait for SCL to go high, return true if successful, false if timeout */
static bool i2c_wait_scl_high(uint16_t max_wait_time) {
while (max_wait_time--) {
if (gpio_input_bit_get(I2C_SCL_PORT, I2C_SCL_PIN)) {
return true;
}
delay_10us(1);
}
return false;
}
/* generate one manual SCL pulse; return true if SCL observed high (no stuck/overstretch) */
static bool i2c_generate_scl_pulse(void) {
GPIO_BC(I2C_SCL_PORT) = I2C_SCL_PIN; /* drive SCL low */
delay_10us(1);
GPIO_BOP(I2C_SCL_PORT) = I2C_SCL_PIN; /* release SCL (open-drain -> high via pull-up) */
return i2c_wait_scl_high(200); /* wait up to ~2ms for clock stretching release */
}
/*!
\brief reset I2C bus
\param[in] none
\param[out] none
\retval none
*/
i2c_result_t i2c_bus_reset(void) {
/* 1. Disable & deinit peripheral so pins can be fully controlled */
i2c_disable(I2C0);
i2c_deinit(I2C0);
#ifdef DEBUG_VERBOSE
printf("I2C bus reset\r\n");
#endif
/* 2. Configure SCL/SDA as GPIO open-drain outputs with pull-up and release them */
gpio_mode_set(I2C_SCL_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, I2C_SCL_PIN);
gpio_mode_set(I2C_SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, I2C_SDA_PIN);
gpio_output_options_set(I2C_SCL_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SCL_PIN);
gpio_output_options_set(I2C_SDA_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SDA_PIN);
gpio_bit_set(I2C_SCL_PORT, I2C_SCL_PIN); /* release SCL */
gpio_bit_set(I2C_SDA_PORT, I2C_SDA_PIN); /* release SDA */
#ifdef DEBUG_VERBOSE
printf("I2C bus reset: SCL = %d, SDA = %d\r\n", gpio_input_bit_get(I2C_SCL_PORT, I2C_SCL_PIN), gpio_input_bit_get(I2C_SDA_PORT, I2C_SDA_PIN));
#endif
/* 3. Double sample to confirm bus state */
delay_10us(1);
bool scl_value1 = gpio_input_bit_get(I2C_SCL_PORT, I2C_SCL_PIN);
bool sda_value1 = gpio_input_bit_get(I2C_SDA_PORT, I2C_SDA_PIN);
delay_10us(1);
bool scl_value2 = gpio_input_bit_get(I2C_SCL_PORT, I2C_SCL_PIN);
bool sda_value2 = gpio_input_bit_get(I2C_SDA_PORT, I2C_SDA_PIN);
/* 4. If SCL low -> stuck (cannot proceed) */
if (!scl_value2) {
#ifdef DEBUG_VERBOSE
printf("I2C bus reset: SCL stuck low\r\n");
#endif
return I2C_RECOVERY_SCL_STUCK_LOW;
}
/* 5. Fast path: bus idle */
if (scl_value1 && sda_value1 && scl_value2 && sda_value2) {
i2c_config();
#ifdef DEBUG_VERBOSE
printf("I2C bus reset: bus idle\r\n");
#endif
return I2C_RECOVERY_OK;
}
/* 6. SDA low: attempt to free by generating up to I2C_RECOVERY_CLOCKS pulses */
if (scl_value2 && !sda_value2) {
bool sda_released = false;
#ifdef DEBUG_VERBOSE
printf("I2C bus reset: SCL will try to free SDA\r\n");
#endif
for (uint8_t i = 0; i < I2C_RECOVERY_CLOCKS && !sda_released; i++) {
if (!i2c_generate_scl_pulse()) {
return I2C_RECOVERY_SCL_STUCK_LOW; /* SCL failed to go high */
}
if (gpio_input_bit_get(I2C_SDA_PORT, I2C_SDA_PIN)) {
sda_released = true;
}
}
if (!sda_released) {
return I2C_RECOVERY_SDA_STUCK_LOW;
}
/* 7. Generate a STOP condition to leave bus in idle state */
#ifdef DEBUG_VERBOSE
printf("I2C bus reset: generating STOP condition\r\n");
#endif
gpio_bit_reset(I2C_SDA_PORT, I2C_SDA_PIN); /* SDA low */
delay_10us(1);
gpio_bit_set(I2C_SCL_PORT, I2C_SCL_PIN); /* ensure SCL high */
delay_10us(1);
gpio_bit_set(I2C_SDA_PORT, I2C_SDA_PIN); /* SDA rising while SCL high -> STOP */
delay_10us(1);
}
#ifdef DEBUG_VERBOSE
printf("I2C bus reset: bus recovered\r\n");
#endif
/* 8. Reconfigure & enable peripheral */
i2c_config();
return I2C_RECOVERY_OK;
}
/**
* @brief 扫描I2C总线查找连接的设备
*
* 该函数会扫描I2C总线上的所有地址1到126并尝试与每个地址进行通信。
* 如果在某个地址上发现了设备,则会打印出该设备的地址。
* 最后会打印出找到的设备总数。
*/
void i2c_scan(void) {
uint32_t timeout;
uint8_t address;
int found_devices = 0;
// printf("Scanning I2C bus...\r\n");
const char* msg1 = "Scanning I2C bus...\r\n";
for (uint8_t i = 0; msg1[i] != '\0'; i++) {
while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TBE) == RESET) {}
usart_data_transmit(I2C_DEBUG_UART, msg1[i]);
}
while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TC) == RESET) {}
for (address = 1; address < 127; address++) {
timeout = 0;
// 生成起始条件
while (i2c_flag_get(I2C0, I2C_FLAG_I2CBSY) && (timeout < I2C_TIME_OUT))
timeout++;
if (timeout >= I2C_TIME_OUT) {
continue; // 超时,跳过该地址
}
i2c_start_on_bus(I2C0);
timeout = 0;
// 等待起始条件发送完成
while (!i2c_flag_get(I2C0, I2C_FLAG_SBSEND) && (timeout < I2C_TIME_OUT))
timeout++;
if (timeout >= I2C_TIME_OUT) {
continue; // 超时,跳过该地址
}
i2c_master_addressing(I2C0, (address << 1), I2C_TRANSMITTER);
timeout = 0;
// 等待地址发送完成
while (!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND) && (timeout < I2C_TIME_OUT))
timeout++;
if (timeout < I2C_TIME_OUT) {
i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND);
// printf("Found device at 0x%02X\r\n", address);
const char* msg2_prefix = "Found device at 0x";
for (uint8_t i = 0; msg2_prefix[i] != '\0'; i++) {
while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TBE) == RESET) {}
usart_data_transmit(I2C_DEBUG_UART, msg2_prefix[i]);
}
// 发送地址的十六进制表示
uint8_t hex_chars[] = "0123456789ABCDEF";
while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TBE) == RESET) {}
usart_data_transmit(I2C_DEBUG_UART, hex_chars[(address >> 4) & 0x0F]);
while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TBE) == RESET) {}
usart_data_transmit(I2C_DEBUG_UART, hex_chars[address & 0x0F]);
const char* msg2_suffix = "\r\n";
for (uint8_t i = 0; msg2_suffix[i] != '\0'; i++) {
while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TBE) == RESET) {}
usart_data_transmit(I2C_DEBUG_UART, msg2_suffix[i]);
}
while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TC) == RESET) {}
found_devices++;
}
// 生成停止条件
i2c_stop_on_bus(I2C0);
timeout = 0;
while (i2c_flag_get(I2C0, I2C_FLAG_STPDET) && (timeout < I2C_TIME_OUT))
timeout++;
}
if (found_devices == 0) {
// printf("No I2C devices found.\r\n");
const char* msg3 = "No I2C devices found.\r\n";
for (uint8_t i = 0; msg3[i] != '\0'; i++) {
while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TBE) == RESET) {}
usart_data_transmit(I2C_DEBUG_UART, msg3[i]);
}
while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TC) == RESET) {}
} else {
// printf("Total %d I2C devices found.\r\n", found_devices);
const char* msg4_prefix = "Total ";
for (uint8_t i = 0; msg4_prefix[i] != '\0'; i++) {
while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TBE) == RESET) {}
usart_data_transmit(I2C_DEBUG_UART, msg4_prefix[i]);
}
// 发送设备数量
if (found_devices >= 10) {
while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TBE) == RESET) {}
usart_data_transmit(I2C_DEBUG_UART, '0' + (found_devices / 10));
}
while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TBE) == RESET) {}
usart_data_transmit(I2C_DEBUG_UART, '0' + (found_devices % 10));
const char* msg4_suffix = " I2C devices found.\r\n";
for (uint8_t i = 0; msg4_suffix[i] != '\0'; i++) {
while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TBE) == RESET) {}
usart_data_transmit(I2C_DEBUG_UART, msg4_suffix[i]);
}
while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TC) == RESET) {}
}
}
i2c_result_t i2c_write_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data[2]) {
i2c_state_t state = I2C_STATE_START;
uint16_t timeout = 0;
uint8_t retry_count = 0;
/* parameter validation */
if (data == NULL || slave_addr > 0x7F) {
return I2C_RESULT_INVALID_PARAM;
}
while (retry_count < I2C_MAX_RETRY) {
switch (state) {
case I2C_STATE_START:
timeout = 0;
/* wait for bus to be idle */
while (i2c_flag_get(I2C0, I2C_FLAG_I2CBSY) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if (timeout >= I2C_TIME_OUT) {
state = I2C_STATE_ERROR;
break;
}
i2c_start_on_bus(I2C0);
timeout = 0;
state = I2C_STATE_SEND_ADDRESS;
break;
case I2C_STATE_SEND_ADDRESS:
/* wait for start condition to be sent. SBSEND flag */
while((!i2c_flag_get(I2C0, I2C_FLAG_SBSEND)) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if (timeout >= I2C_TIME_OUT) {
state = I2C_STATE_ERROR;
break;
}
/* send slave address */
i2c_master_addressing(I2C0, slave_addr << 1, I2C_TRANSMITTER);
timeout = 0;
state = I2C_STATE_CLEAR_ADDRESS;
break;
case I2C_STATE_CLEAR_ADDRESS:
/* wait for address to be acknowledged.ADDSEND set means i2c slave sends ACK */
while ((!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND)) && (!i2c_flag_get(I2C0, I2C_FLAG_AERR)) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if (timeout >= I2C_TIME_OUT) {
state = I2C_STATE_ERROR;
break;
} else if (i2c_flag_get(I2C0, I2C_FLAG_ADDSEND))
{
i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND);
timeout =0;
state = I2C_STATE_TRANSMIT_REG;
break;
} else {
i2c_flag_clear(I2C0, I2C_FLAG_AERR);
timeout =0;
#ifdef DEBUG_VERBOES
printf("IIC write failed for Error Slave Address. \n");
#endif
return I2C_RESULT_NACK;
}
case I2C_STATE_TRANSMIT_REG:
/* wait until the transmit data buffer is empty */
while ((!i2c_flag_get(I2C0, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if (timeout >= I2C_TIME_OUT) {
state = I2C_STATE_ERROR;
break;
}
/* send register address */
i2c_data_transmit(I2C0, reg_addr);
timeout = 0;
state = I2C_STATE_TRANSMIT_DATA;
break;
case I2C_STATE_TRANSMIT_DATA:
/* wait until the transmit data buffer is empty */
while ((!i2c_flag_get(I2C0, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if (timeout >= I2C_TIME_OUT) {
state = I2C_STATE_ERROR;
break;
}
/* send register MSB value */
i2c_data_transmit(I2C0, data[0]);
timeout = 0;
/* wait until the transmit data buffer is empty */
while ((!i2c_flag_get(I2C0, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if (timeout >= I2C_TIME_OUT) {
state = I2C_STATE_ERROR;
break;
}
if (i2c_flag_get(I2C0, I2C_FLAG_AERR)) {
i2c_stop_on_bus(I2C0);
return I2C_RESULT_NACK;
} else if (i2c_flag_get(I2C0, I2C_FLAG_BERR) || i2c_flag_get(I2C0, I2C_FLAG_LOSTARB)) {
// 可按需清标志
i2c_stop_on_bus(I2C0);
return I2C_RESULT_ERROR;
}
/* send register LSB value */
i2c_data_transmit(I2C0, data[1]);
timeout = 0;
/* wait until BTC bit is set */
while (!i2c_flag_get(I2C0, I2C_FLAG_BTC) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if (timeout >= I2C_TIME_OUT) {
state = I2C_STATE_ERROR;
break;
}
state = I2C_STATE_STOP;
break;
case I2C_STATE_STOP:
/* send a stop condition to I2C bus */
i2c_stop_on_bus(I2C0);
timeout = 0;
while ((I2C_CTL0(I2C0) & I2C_CTL0_STOP) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if (timeout >= I2C_TIME_OUT) {
state = I2C_STATE_ERROR;
break;
}
/* i2c master sends STOP signal successfully */
/* success */
return I2C_RESULT_SUCCESS;
case I2C_STATE_ERROR:
/* send a stop condition to I2C bus */
i2c_stop_on_bus(I2C0);
timeout = 0;
while ((I2C_CTL0(I2C0) & I2C_CTL0_STOP) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if (timeout >= I2C_TIME_OUT) {
return I2C_RESULT_ERROR;
}
i2c_flag_clear(I2C0, I2C_FLAG_AERR);
i2c_flag_clear(I2C0, I2C_FLAG_BERR);
i2c_flag_clear(I2C0, I2C_FLAG_LOSTARB);
retry_count ++;
if (retry_count >= I2C_MAX_RETRY)
{
#ifdef DEBUG_VERBOES
printf("IIC write failed after %d retries\n", I2C_MAX_RETRY);
#endif
return I2C_RESULT_ERROR;
}
/* reset state machine for retry */
state = I2C_STATE_START;
timeout = 0;
/* small delay before retry */
delay_10us(10);
break;
default:
state = I2C_STATE_START;
break;
}
}
return I2C_RESULT_TIMEOUT;
}
i2c_result_t i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data) {
i2c_state_t state = I2C_STATE_START;
uint16_t timeout = 0;
uint8_t retry_count = 0;
bool write_phase = true;
// 参数检查:防止空指针和非法地址
if (data == NULL || slave_addr > 0x7F) {
return I2C_RESULT_INVALID_PARAM;
}
/* enable acknowledge */
i2c_ack_config(I2C0, I2C_ACK_ENABLE);
while (retry_count < (uint8_t)I2C_MAX_RETRY) {
switch (state) {
case I2C_STATE_START:
timeout = 0;
// wait for bus to be idle
while (i2c_flag_get(I2C0, I2C_FLAG_I2CBSY) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if (timeout >= I2C_TIME_OUT) {
state = I2C_STATE_ERROR;
break;
}
// send start condition
i2c_start_on_bus(I2C0);
state = I2C_STATE_SEND_ADDRESS;
timeout = 0;
break;
case I2C_STATE_SEND_ADDRESS:
/* wait for start condition to be sent */
while ((!i2c_flag_get(I2C0, I2C_FLAG_SBSEND)) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if (timeout >= I2C_TIME_OUT) {
state = I2C_STATE_ERROR;
break;
}
// send slave address
if (write_phase) {
/* write phase: send address with write bit */
i2c_master_addressing(I2C0, (slave_addr << 1), I2C_TRANSMITTER);
} else {
/* read phase: send address with read bit */
i2c_master_addressing(I2C0, (slave_addr << 1) | 0x01, I2C_RECEIVER);
}
state = I2C_STATE_CLEAR_ADDRESS;
timeout = 0;
break;
case I2C_STATE_CLEAR_ADDRESS:
/* wait for address to be acknowledged */
while ((!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND)) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if (timeout >= I2C_TIME_OUT) {
state = I2C_STATE_ERROR;
break;
}
if (write_phase) {
/* clear address flag (write phase) */
i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND);
state = I2C_STATE_TRANSMIT_DATA;
} else {
/* READ phase for 2 bytes: set POS=NEXT and disable ACK BEFORE clearing ADDR */
i2c_ackpos_config(I2C0, I2C_ACKPOS_NEXT);
i2c_ack_config(I2C0, I2C_ACK_DISABLE);
/* now clear address flag to release SCL and enter data phase */
i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND);
state = I2C_STATE_RECEIVE_DATA;
}
timeout = 0;
break;
case I2C_STATE_TRANSMIT_DATA:
/* wait for transmit buffer to be empty */
while ((!i2c_flag_get(I2C0, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if (timeout >= I2C_TIME_OUT) {
state = I2C_STATE_ERROR;
break;
}
/* send register address */
i2c_data_transmit(I2C0, reg_addr);
state = I2C_STATE_RESTART;
timeout = 0;
break;
case I2C_STATE_RESTART:
/* wait for byte transfer complete BTC: Bit Transfer Complete */
while ((!i2c_flag_get(I2C0, I2C_FLAG_BTC)) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if (timeout >= I2C_TIME_OUT) {
state = I2C_STATE_ERROR;
break;
}
/* generate repeated start condition */
i2c_start_on_bus(I2C0);
/* wait for repeated start condition to be sent */
timeout = 0;
while ((!i2c_flag_get(I2C0, I2C_FLAG_SBSEND)) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if (timeout >= I2C_TIME_OUT) {
state = I2C_STATE_ERROR;
break;
}
/* send slave address with read bit (R/W bit is set by library) */
i2c_master_addressing(I2C0, (slave_addr << 1), I2C_RECEIVER);
/* switch to read phase */
write_phase = false;
state = I2C_STATE_CLEAR_ADDRESS;
timeout = 0;
break;
case I2C_STATE_RECEIVE_DATA:
/* Wait for BTC (both bytes received) */
while ((!i2c_flag_get(I2C0, I2C_FLAG_BTC)) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if (timeout >= I2C_TIME_OUT) {
state = I2C_STATE_ERROR;
break;
}
/* Send STOP before reading the last two bytes */
i2c_stop_on_bus(I2C0);
/* Read the two bytes back-to-back */
data[0] = i2c_data_receive(I2C0);
data[1] = i2c_data_receive(I2C0);
state = I2C_STATE_STOP;
break;
case I2C_STATE_STOP:
/* wait for stop condition to complete */
while ((I2C_CTL0(I2C0) & I2C_CTL0_STOP) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if (timeout >= I2C_TIME_OUT) {
state = I2C_STATE_ERROR;
break;
}
/* i2c master sends STOP signal successfully */
/* success */
return I2C_RESULT_SUCCESS;
case I2C_STATE_ERROR:
/* send stop condition to release bus */
i2c_stop_on_bus(I2C0);
retry_count++;
if (retry_count >= I2C_MAX_RETRY) {
#ifdef DEBUG_VERBOES
printf("IIC read failed after %d retries\n", I2C_RETRY_MAX);
#endif
return I2C_RESULT_ERROR;
}
/* reset state machine for retry */
state = I2C_STATE_START;
write_phase = true;
timeout = 0;
/* small delay before retry */
delay_10us(10);
break;
default:
state = I2C_STATE_START;
break;
}
}
return I2C_RESULT_TIMEOUT;
}
#ifdef DEBUG_VERBOSE
/*!
\brief get status string for debugging
\param[in] status: i2c_status_t value
\param[out] none
\retval const char* status string
*/
const char* i2c_get_status_string(i2c_result_t status) {
switch (status) {
case I2C_RESULT_SUCCESS:
return "SUCCESS";
case I2C_RESULT_TIMEOUT:
return "TIMEOUT";
case I2C_RESULT_NACK:
return "NACK";
case I2C_RESULT_BUS_BUSY:
return "BUS_BUSY";
case I2C_RESULT_ERROR:
return "ERROR";
case I2C_RESULT_INVALID_PARAM:
return "INVALID_PARAM";
default:
return "UNKNOWN";
}
}
#endif

View File

@@ -1,57 +0,0 @@
#include "led.h"
/**
* @brief LED心跳指示灯功能
* @details 实现类似心跳的LED闪烁模式快闪两次然后暂停
* 适合在SysTick中断中调用通过计数器控制闪烁节拍
* @note 假设SysTick中断频率为1ms心跳周期约为2秒
* 心跳模式亮200ms->灭200ms->亮200ms->灭1400ms循环
*/
void led_heart_beat(void)
{
static uint16_t heart_beat_counter = 0;
// 心跳周期2000ms (假设SysTick为1ms中断)
// 模式亮200ms -> 灭200ms -> 亮200ms -> 灭1400ms
heart_beat_counter++;
if (heart_beat_counter <= 200) {
// 第一次亮0-200ms
led_on();
}
else if (heart_beat_counter <= 400) {
// 第一次灭200-400ms
led_off();
}
else if (heart_beat_counter <= 600) {
// 第二次亮400-600ms
led_on();
}
else if (heart_beat_counter <= 2000) {
// 长时间灭600-2000ms
led_off();
}
else {
// 重置计数器,开始新的心跳周期
heart_beat_counter = 0;
}
}
void led_init(void) {
rcu_periph_clock_enable(LED_RCU);
gpio_mode_set(LED_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_PIN);
gpio_output_options_set(LED_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, LED_PIN);
gpio_bit_set(LED_PORT, LED_PIN);
}
void led_on(void) {
gpio_bit_reset(LED_PORT, LED_PIN);
}
void led_off(void) {
gpio_bit_set(LED_PORT, LED_PIN);
}
void led_toggle(void) {
gpio_bit_toggle(LED_PORT, LED_PIN);
}

View File

@@ -35,11 +35,10 @@ OF SUCH DAMAGE.
#include "gd32e23x.h"
#include "systick.h"
#include "uart.h"
#include "led.h"
#include "command.h"
#include <stdio.h>
#include "i2c.h"
#include "uart_ring_buffer.h"
#include "board_config.h"
#include "gpio.h"
#include "bootloader.h"
/*!
\brief main function
@@ -49,29 +48,39 @@ OF SUCH DAMAGE.
*/
int main(void)
{
led_init();
mcu_detect_and_config();
uint8_t buf[1];
uart_ring_buffer_init();
if (rcu_flag_get(RCU_FLAG_SWRST) == RESET)
{
check_flash_and_jump((uint8_t*)FLASH_FLAG_ADDRESS);
}
setbuf(stdout, NULL);
systick_config();
gpio_init();
rs485_init();
printf("Flash size: %d Kbytes\n", get_flash_size());
// gpio_bit_write(MOTOR_EN_PORT, MOTOR_EN_PIN, RESET);
#ifdef DEBUG_VERBOSE
printf("Hello World!\r\n");
#endif
delay_ms(100);
rs485_send_str((uint8_t*)"ok", 2);
delay_ms(100);
// wait rs485 send complete: DE high -> low
i2c_config();
while (uart_ring_buffer_get() != 0x30);
rs485_send_str((uint8_t*)"ok", 2);
#ifdef DEBUG_VERBOSE
i2c_scan();
while (uart_ring_buffer_get() != 0x31);
rs485_send_str((uint8_t*)"ok", 2);
i2c_bus_reset();
#endif
ymodem_receive(buf);
delay_ms(100);
check_flash_and_jump((uint8_t*)FLASH_FLAG_ADDRESS);
while(1){
command_process();
delay_ms(10);
}
}

View File

@@ -7,8 +7,6 @@
void rs485_init(void) {
#ifndef RS485_MAX13487
/* 使能 GPIOA 和 USART0 时钟 */
rcu_periph_clock_enable(RS485_GPIO_RCU);
rcu_periph_clock_enable(RS485_RCU);
@@ -36,72 +34,38 @@ void rs485_init(void) {
usart_rs485_driver_enable(RS485_PHY);
usart_enable(RS485_PHY);
nvic_irq_enable(RS485_IRQ, 0);
usart_interrupt_enable(RS485_PHY, USART_INT_RBNE);
// usart_interrupt_enable(RS485_PHY, USART_INT_IDLE);
#else
rcu_periph_clock_enable(RS485_GPIO_RCU);
rcu_periph_clock_enable(RS485_RCU);
gpio_af_set(RS485_GPIO_PORT, GPIO_AF_1, GPIO_PIN_2 | GPIO_PIN_3);
/* configure USART Tx&Rx as alternate function push-pull */
gpio_mode_set(RS485_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, RS485_TX_PIN | RS485_RX_PIN);
gpio_output_options_set(RS485_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, RS485_TX_PIN | RS485_RX_PIN);
/* configure RS485 EN Pin */
gpio_mode_set(RS485_GPIO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, RS485_EN_PIN);
gpio_output_options_set(RS485_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, RS485_EN_PIN);
gpio_bit_write(RS485_GPIO_PORT, RS485_EN_PIN, SET);
/* USART configure */
usart_deinit(RS485_PHY);
usart_baudrate_set(RS485_PHY, RS485_BAUDRATE);
usart_receive_config(RS485_PHY, USART_RECEIVE_ENABLE);
usart_transmit_config(RS485_PHY, USART_TRANSMIT_ENABLE);
nvic_irq_enable(RS485_IRQ, 0);
usart_enable(RS485_PHY);
nvic_irq_enable(USART0_IRQn, 0);
usart_interrupt_enable(RS485_PHY, USART_INT_RBNE);
usart_interrupt_enable(RS485_PHY, USART_INT_IDLE);
#endif // RS485_MAX13487
}
/******************************************************************************/
/* 具体的中断处理函数实现 */
/******************************************************************************/
uint32_t rs485_send_byte(uint8_t data) {
// 等待发送缓冲区空
while (RESET == usart_flag_get(RS485_PHY, USART_FLAG_TBE));
void usart0_irq_handler(void) {
// 处理USART0的接收中断
if(usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)) {
uint8_t data = usart_data_receive(USART0);
// 使用原有的环形缓冲区处理逻辑
(void)uart_ring_buffer_put(data); // 缓冲满时丢弃,返回值可用于统计
// 发送数据
usart_data_transmit(RS485_PHY, data);
// 等待发送完成
while (RESET == usart_flag_get(RS485_PHY, USART_FLAG_TC));
return 0; // 成功
}
// 处理USART0的空闲中断
if(usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE)) {
usart_interrupt_flag_clear(USART0, USART_INT_FLAG_IDLE);
// 在这里添加空闲中断处理逻辑
}
uint32_t rs485_send_str(uint8_t* str, uint16_t len) {
// 发送数据 - 最优化版本,避免索引变量
uint8_t* end = str + len;
while (str < end) {
// 等待发送缓冲区空
while (RESET == usart_flag_get(RS485_PHY, USART_FLAG_TBE));
usart_data_transmit(RS485_PHY, *str++);
}
void usart1_irq_handler(void) {
// 处理USART1的接收中断
if(usart_interrupt_flag_get(USART1, USART_INT_FLAG_RBNE)) {
uint8_t data = usart_data_receive(USART1);
// 使用原有的环形缓冲区处理逻辑
(void)uart_ring_buffer_put(data); // 缓冲满时丢弃,返回值可用于统计
}
// 等待最后一个字节发送完成(重要!)
while (RESET == usart_flag_get(RS485_PHY, USART_FLAG_TC));
// 处理USART1的空闲中断
if(usart_interrupt_flag_get(USART1, USART_INT_FLAG_IDLE)) {
usart_interrupt_flag_clear(USART1, USART_INT_FLAG_IDLE);
// 在这里添加空闲中断处理逻辑
}
return 0; // 成功
}

View File

@@ -1,104 +1,58 @@
/**
* @file uart_ring_buffer.c
* @brief 字节环形接收缓冲区的实现。
* @details 适用于中断接收(写)与主循环解析(读)的典型串口场景;
* 采用“预留一格”区分空/满,最大可用容量为 UART_RX_BUFFER_SIZE-1。
* @ingroup RingBuffer
*/
#include "uart_ring_buffer.h"
static volatile uint8_t uart_rx_buffer[UART_RX_BUFFER_SIZE];
static volatile uint8_t write_index = 0;
static volatile uint8_t read_index = 0;
static volatile uint32_t dropped_bytes = 0;
// 环形缓冲区结构体定义(精简版)
struct uart_ring_buffer {
volatile uint8_t buffer[UART_RX_BUFFER_SIZE];
volatile uint8_t head; // 写指针
volatile uint8_t tail; // 读指针
};
static uart_ring_buffer_t uart_rx_buf = {0};
/**
* @brief 重置环形缓冲区状态。
* @details 将读指针、写指针与丢弃计数清零,不清空数据区内容。
* @note 内部工具函数;对外请优先使用 uart_ring_buffer_init()/uart_ring_buffer_clear()。
* @ingroup RingBuffer
*/
static void uart_ring_buffer_reset_state(void) {
write_index = 0;
read_index = 0;
dropped_bytes = 0;
uart_rx_buf.head = 0;
uart_rx_buf.tail = 0;
}
/**
* @brief 初始化环形缓冲区。
* @details 调用内部重置逻辑,复位读写索引与丢弃计数,准备接收数据。
* @note 若在中断环境使用,初始化前建议关闭相关接收中断以避免并发竞争。
* @ingroup RingBuffer
*/
void uart_ring_buffer_init(void) {
uart_ring_buffer_reset_state();
}
/**
* @brief 获取当前可读的字节数。
* @details 通过读/写指针的快照计算可读长度,范围为 [0, UART_RX_BUFFER_SIZE-1]。
* @return uint8_t 可读字节数。
* @note 预留一个空槽区分“空/满”,因此满时返回 UART_RX_BUFFER_SIZE-1。
* @ingroup RingBuffer
*/
uint8_t uart_ring_buffer_available(void) {
/* 使用快照减少并发不一致窗口 */
uint8_t w = write_index;
uint8_t r = read_index;
return (uint8_t)((w + UART_RX_BUFFER_SIZE - r) % UART_RX_BUFFER_SIZE);
uint8_t h = uart_rx_buf.head;
uint8_t t = uart_rx_buf.tail;
return (uint8_t)((h + UART_RX_BUFFER_SIZE - t) % UART_RX_BUFFER_SIZE);
}
/**
* @brief 从环形缓冲区取一个字节。
* @details 若缓冲区非空,返回队头字节并推进读指针;若为空,返回 -1。
* @return int 读取到的字节0..255),或 -1 表示缓冲区为空。
* @ingroup RingBuffer
* @brief 从UART环形缓冲区中获取一个数据
*
* @return int 成功返回获取到的8位数据如果缓冲区为空则返回-1
*/
int uart_ring_buffer_get(void) {
if (read_index == write_index) return -1; // 空
uint8_t data = uart_rx_buffer[read_index];
read_index = (read_index + 1) % UART_RX_BUFFER_SIZE;
// 检查环形缓冲区是否为空tail与head相等表示缓冲区为空
if (uart_rx_buf.tail == uart_rx_buf.head) return -1;
// 从缓冲区tail位置获取数据
uint8_t data = uart_rx_buf.buffer[uart_rx_buf.tail];
// 更新tail位置实现环形缓冲区的循环使用
// 使用取模运算确保tail在缓冲区大小范围内循环
uart_rx_buf.tail = (uart_rx_buf.tail + 1) % UART_RX_BUFFER_SIZE;
// 返回获取到的数据
return data;
}
/**
* @brief 向环形缓冲区写入一个字节。
* @details 尝试写入一个新字节;若缓冲区已满则丢弃并计数。
* @param data 待写入的字节。
* @return bool 是否写入成功。
* @retval true 写入成功。
* @retval false 写入失败(缓冲区已满,数据被丢弃并计数)。
* @note 如需“覆盖写入”策略,可在满时先推进读指针再写入。
* @ingroup RingBuffer
*/
bool uart_ring_buffer_put(uint8_t data) {
uint8_t next = (write_index + 1) % UART_RX_BUFFER_SIZE;
if (next != read_index) { // 缓冲区未满
uart_rx_buffer[write_index] = data;
write_index = next;
uint8_t next = (uart_rx_buf.head + 1) % UART_RX_BUFFER_SIZE;
if (next != uart_rx_buf.tail) {
uart_rx_buf.buffer[uart_rx_buf.head] = data;
uart_rx_buf.head = next;
return true;
} else {
/* 缓冲区满,丢弃新字节并计数 */
dropped_bytes++;
// 缓冲区满,静默丢弃新数据
return false;
}
}
/**
* @brief 清空环形缓冲区。
* @details 复位读写索引与丢弃计数,相当于逻辑上丢弃所有已接收数据,不擦除数据区内容。
* @ingroup RingBuffer
*/
void uart_ring_buffer_clear(void) {
uart_ring_buffer_reset_state();
}
/**
* @brief 获取因满而被丢弃的字节累计数量。
* @details 写入时缓冲区满会丢弃新字节并累加计数;该计数在 init/clear 时清零。
* @return uint32_t 丢弃的累计字节数。
* @ingroup RingBuffer
*/
uint32_t uart_ring_buffer_drop_count(void) {
return dropped_bytes;
}

View File

@@ -24,7 +24,7 @@ if(DEFINED TOOLCHAIN_DIRECTORY)
set(TOOLCHAIN_PREFIX "${TOOLCHAIN_DIRECTORY}/${TOOLCHAIN_PREFIX}")
endif()
set(FLAGS "-mcpu=cortex-m23 -std=gnu11 -fstack-usage -fdata-sections -ffunction-sections -fmessage-length=0 -fsigned-char -mthumb -Wall -Wno-missing-braces -Wno-format -Wno-strict-aliasing -Wl,--gc-sections")
set(FLAGS "-mcpu=cortex-m23 -std=gnu11 -Os -fstack-usage -fdata-sections -ffunction-sections -fmessage-length=0 -fsigned-char -mthumb -Wall -Wno-missing-braces -Wno-format -Wno-strict-aliasing -Wl,--gc-sections -flto")
set(ASM_FLAGS "-x assembler-with-cpp")
set(CPP_FLAGS "-fno-rtti -fno-exceptions -fno-threadsafe-statics")

View File

@@ -16,8 +16,8 @@ target_compile_options(${TARGET_NAME} PRIVATE
"$<$<AND:$<CONFIG:Debug>,$<COMPILE_LANGUAGE:C>>:-O0>"
"$<$<AND:$<CONFIG:Debug>,$<COMPILE_LANGUAGE:CXX>>:-O0>"
"$<$<AND:$<NOT:$<CONFIG:Debug>>,$<COMPILE_LANGUAGE:C>>:-Os>"
"$<$<AND:$<NOT:$<CONFIG:Debug>>,$<COMPILE_LANGUAGE:CXX>>:-Os>"
"$<$<AND:$<NOT:$<CONFIG:Debug>>,$<COMPILE_LANGUAGE:C>>:-Os;-flto>"
"$<$<AND:$<NOT:$<CONFIG:Debug>>,$<COMPILE_LANGUAGE:CXX>>:-Os;-flto>"
-mcpu=cortex-m23
)
@@ -30,6 +30,7 @@ target_link_options(${TARGET_NAME} PRIVATE
--specs=nano.specs
--specs=nosys.specs
-Wl,--gc-sections
"$<$<NOT:$<CONFIG:Debug>>:-flto>"
-Wl,--start-group -lc -lm -Wl,--end-group
)