generated from hulk/gd32e23x_template_cmake_vscode
Follow the reference to port the bootloader
This commit is contained in:
@@ -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
|
||||
)
|
||||
|
||||
# 设置输出目录
|
||||
|
@@ -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
57
Inc/bootloader.h
Normal 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
|
@@ -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
6
Inc/gpio.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef GPIO_H
|
||||
#define GPIO_H
|
||||
|
||||
void gpio_init(void);
|
||||
|
||||
#endif // GPIO_H
|
127
Inc/i2c.h
127
Inc/i2c.h
@@ -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
|
13
Inc/led.h
13
Inc/led.h
@@ -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
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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 */
|
||||
|
@@ -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
|
||||
|
@@ -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
476
Src/bootloader.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
403
Src/command.c
403
Src/command.c
@@ -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), ¶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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
// 在这里添加空闲中断处理逻辑
|
||||
}
|
||||
}
|
||||
|
33
Src/gpio.c
Normal file
33
Src/gpio.c
Normal 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
677
Src/i2c.c
@@ -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
|
57
Src/led.c
57
Src/led.c
@@ -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);
|
||||
}
|
45
Src/main.c
45
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 <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);
|
||||
|
||||
}
|
||||
}
|
||||
|
86
Src/uart.c
86
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); // 缓冲满时丢弃,返回值可用于统计
|
||||
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++);
|
||||
}
|
||||
|
||||
// 处理USART1的空闲中断
|
||||
if(usart_interrupt_flag_get(USART1, USART_INT_FLAG_IDLE)) {
|
||||
usart_interrupt_flag_clear(USART1, USART_INT_FLAG_IDLE);
|
||||
// 在这里添加空闲中断处理逻辑
|
||||
}
|
||||
// 等待最后一个字节发送完成(重要!)
|
||||
while (RESET == usart_flag_get(RS485_PHY, USART_FLAG_TC));
|
||||
|
||||
return 0; // 成功
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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")
|
||||
|
||||
|
@@ -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
|
||||
)
|
||||
|
||||
|
Reference in New Issue
Block a user