From bd541d585e2c13f8387cde417ac697b32bc84c60 Mon Sep 17 00:00:00 2001 From: yelvlab Date: Sun, 28 Sep 2025 01:14:06 +0800 Subject: [PATCH] Follow the reference to port the bootloader --- CMakeLists.txt | 6 +- Inc/board_config.h | 45 +- Inc/bootloader.h | 57 ++ Inc/command.h | 81 --- Inc/gpio.h | 6 + Inc/i2c.h | 127 ---- Inc/led.h | 13 - Inc/uart.h | 4 + Inc/uart_ring_buffer.h | 14 +- LD/gd32e23x_flash.ld | 2 +- .../CMakeLists.txt | 26 +- Src/board_config.c | 52 -- Src/bootloader.c | 476 ++++++++++++ Src/command.c | 403 ----------- Src/gd32e23x_it.c | 23 +- Src/gpio.c | 33 + Src/i2c.c | 677 ------------------ Src/led.c | 57 -- Src/main.c | 45 +- Src/uart.c | 92 +-- Src/uart_ring_buffer.c | 104 +-- cmake/arm-none-eabi-gcc.cmake | 2 +- cmake/project.cmake | 5 +- 23 files changed, 712 insertions(+), 1638 deletions(-) create mode 100644 Inc/bootloader.h delete mode 100644 Inc/command.h create mode 100644 Inc/gpio.h delete mode 100644 Inc/i2c.h delete mode 100644 Inc/led.h delete mode 100644 Src/board_config.c create mode 100644 Src/bootloader.c delete mode 100644 Src/command.c create mode 100644 Src/gpio.c delete mode 100644 Src/i2c.c delete mode 100644 Src/led.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a65f81..4906311 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 ) # 设置输出目录 diff --git a/Inc/board_config.h b/Inc/board_config.h index 13fc6c9..2aaa572 100644 --- a/Inc/board_config.h +++ b/Inc/board_config.h @@ -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 /* >>>>>>>>>>>>>>>>>>>>[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 diff --git a/Inc/bootloader.h b/Inc/bootloader.h new file mode 100644 index 0000000..4422383 --- /dev/null +++ b/Inc/bootloader.h @@ -0,0 +1,57 @@ +#ifndef BOOTLOADER_H +#define BOOTLOADER_H + +#include + +#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 \ No newline at end of file diff --git a/Inc/command.h b/Inc/command.h deleted file mode 100644 index 56c4385..0000000 --- a/Inc/command.h +++ /dev/null @@ -1,81 +0,0 @@ -/** - * @file command.h - * @brief 串口命令解析与处理模块接口声明。 - * @details 提供基于环形缓冲区的串口协议解析、命令处理及状态管理功能, - * 支持格式为 D5 03 LEN [cmd] CRC 的命令帧解析与响应。 - */ -#ifndef COMMAND_H -#define COMMAND_H - -#include -#include - -/** - * @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 diff --git a/Inc/gpio.h b/Inc/gpio.h new file mode 100644 index 0000000..ed32af9 --- /dev/null +++ b/Inc/gpio.h @@ -0,0 +1,6 @@ +#ifndef GPIO_H +#define GPIO_H + +void gpio_init(void); + +#endif // GPIO_H diff --git a/Inc/i2c.h b/Inc/i2c.h deleted file mode 100644 index e810478..0000000 --- a/Inc/i2c.h +++ /dev/null @@ -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 -#include -#include -#include -#include - -#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 diff --git a/Inc/led.h b/Inc/led.h deleted file mode 100644 index 7376a5e..0000000 --- a/Inc/led.h +++ /dev/null @@ -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 diff --git a/Inc/uart.h b/Inc/uart.h index b7edabc..4c9d96d 100644 --- a/Inc/uart.h +++ b/Inc/uart.h @@ -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 diff --git a/Inc/uart_ring_buffer.h b/Inc/uart_ring_buffer.h index effeb35..8b91901 100644 --- a/Inc/uart_ring_buffer.h +++ b/Inc/uart_ring_buffer.h @@ -10,12 +10,14 @@ #include #include +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 diff --git a/LD/gd32e23x_flash.ld b/LD/gd32e23x_flash.ld index 405de65..79c0522 100644 --- a/LD/gd32e23x_flash.ld +++ b/LD/gd32e23x_flash.ld @@ -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 */ diff --git a/SDK/GD32E23x_standard_peripheral/CMakeLists.txt b/SDK/GD32E23x_standard_peripheral/CMakeLists.txt index 4db994c..47e3012 100644 --- a/SDK/GD32E23x_standard_peripheral/CMakeLists.txt +++ b/SDK/GD32E23x_standard_peripheral/CMakeLists.txt @@ -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 diff --git a/Src/board_config.c b/Src/board_config.c deleted file mode 100644 index a68f73e..0000000 --- a/Src/board_config.c +++ /dev/null @@ -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; -} diff --git a/Src/bootloader.c b/Src/bootloader.c new file mode 100644 index 0000000..4a3a22d --- /dev/null +++ b/Src/bootloader.c @@ -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; + } + } + } +} \ No newline at end of file diff --git a/Src/command.c b/Src/command.c deleted file mode 100644 index a134692..0000000 --- a/Src/command.c +++ /dev/null @@ -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 -#include -#include -#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: 设置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), ¶m_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; - } - } -} diff --git a/Src/gd32e23x_it.c b/Src/gd32e23x_it.c index a640c63..b36f5d3 100644 --- a/Src/gd32e23x_it.c +++ b/Src/gd32e23x_it.c @@ -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(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_RBNE)) { + uint8_t data = usart_data_receive(USART1); + // 使用原有的环形缓冲区处理逻辑 + (void)uart_ring_buffer_put(data); // 缓冲满时丢弃,返回值可用于统计 + } + + // 处理USART1的空闲中断 + if(usart_interrupt_flag_get(USART1, USART_INT_FLAG_IDLE)) { + usart_interrupt_flag_clear(USART1, USART_INT_FLAG_IDLE); + // 在这里添加空闲中断处理逻辑 } } diff --git a/Src/gpio.c b/Src/gpio.c new file mode 100644 index 0000000..40132ca --- /dev/null +++ b/Src/gpio.c @@ -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); +} \ No newline at end of file diff --git a/Src/i2c.c b/Src/i2c.c deleted file mode 100644 index e241b58..0000000 --- a/Src/i2c.c +++ /dev/null @@ -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 diff --git a/Src/led.c b/Src/led.c deleted file mode 100644 index 1661867..0000000 --- a/Src/led.c +++ /dev/null @@ -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); -} diff --git a/Src/main.c b/Src/main.c index 4d2370c..df4ef7f 100644 --- a/Src/main.c +++ b/Src/main.c @@ -35,11 +35,10 @@ OF SUCH DAMAGE. #include "gd32e23x.h" #include "systick.h" #include "uart.h" -#include "led.h" -#include "command.h" -#include -#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); + } } diff --git a/Src/uart.c b/Src/uart.c index 10f27ee..ed54489 100644 --- a/Src/uart.c +++ b/Src/uart.c @@ -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 - } - -/******************************************************************************/ -/* 具体的中断处理函数实现 */ -/******************************************************************************/ - -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); // 缓冲满时丢弃,返回值可用于统计 - } - - // 处理USART0的空闲中断 - if(usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE)) { - usart_interrupt_flag_clear(USART0, USART_INT_FLAG_IDLE); - // 在这里添加空闲中断处理逻辑 - } } -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); // 缓冲满时丢弃,返回值可用于统计 - } - - // 处理USART1的空闲中断 - if(usart_interrupt_flag_get(USART1, USART_INT_FLAG_IDLE)) { - usart_interrupt_flag_clear(USART1, USART_INT_FLAG_IDLE); - // 在这里添加空闲中断处理逻辑 - } +uint32_t rs485_send_byte(uint8_t data) { + // 等待发送缓冲区空 + while (RESET == usart_flag_get(RS485_PHY, USART_FLAG_TBE)); + + // 发送数据 + usart_data_transmit(RS485_PHY, data); + + // 等待发送完成 + while (RESET == usart_flag_get(RS485_PHY, USART_FLAG_TC)); + + return 0; // 成功 +} + +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++); + } + + // 等待最后一个字节发送完成(重要!) + while (RESET == usart_flag_get(RS485_PHY, USART_FLAG_TC)); + + return 0; // 成功 } diff --git a/Src/uart_ring_buffer.c b/Src/uart_ring_buffer.c index 73554f0..6ebde88 100644 --- a/Src/uart_ring_buffer.c +++ b/Src/uart_ring_buffer.c @@ -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; -} diff --git a/cmake/arm-none-eabi-gcc.cmake b/cmake/arm-none-eabi-gcc.cmake index e8ef9c9..9b60629 100644 --- a/cmake/arm-none-eabi-gcc.cmake +++ b/cmake/arm-none-eabi-gcc.cmake @@ -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") diff --git a/cmake/project.cmake b/cmake/project.cmake index 81f6dc6..d16dc1a 100644 --- a/cmake/project.cmake +++ b/cmake/project.cmake @@ -16,8 +16,8 @@ target_compile_options(${TARGET_NAME} PRIVATE "$<$,$>:-O0>" "$<$,$>:-O0>" - "$<$>,$>:-Os>" - "$<$>,$>:-Os>" + "$<$>,$>:-Os;-flto>" + "$<$>,$>:-Os;-flto>" -mcpu=cortex-m23 ) @@ -30,6 +30,7 @@ target_link_options(${TARGET_NAME} PRIVATE --specs=nano.specs --specs=nosys.specs -Wl,--gc-sections + "$<$>:-flto>" -Wl,--start-group -lc -lm -Wl,--end-group )