Initial commit

This commit is contained in:
2025-08-19 22:53:06 +08:00
commit 1247429882
101 changed files with 54027 additions and 0 deletions

407
Src/command.c Normal file
View File

@@ -0,0 +1,407 @@
/**
* @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: enable sensor report
send_response(RESP_TYPE_OK, s_report_status_ok, sizeof(s_report_status_ok));
return;
case 2u: // M2: disable sensor report
send_response(RESP_TYPE_OK, s_report_status_ok, sizeof(s_report_status_ok));
return;
case 3u:
send_response(RESP_TYPE_OK, s_report_status_ok, sizeof(s_report_status_ok));
return;
case 4u:
send_response(RESP_TYPE_OK, s_report_status_ok, sizeof(s_report_status_ok));
return;
// case 201u: // M201命令
// send_response(RESP_TYPE_OK, s_report_status_ok, sizeof(s_report_status_ok));
// return;
default:
// 其它无参数命令在此扩展示例M100处理逻辑该如何待定
// send_response(RESP_TYPE_OK, s_report_status_ok, sizeof(s_report_status_ok));
// return;
break;
}
// 未在处理列表的无参数基础命令,回复错误
send_response(RESP_TYPE_TYPE_ERR, s_report_status_err, sizeof(s_report_status_err));
return;
}
// 情况B有附加参数的命令
if (cmd[cmd_index] == 'S') {
cmd_index++;
uint32_t param_value = 0;
const uint8_t used_param_cmd = parse_uint_dec(&cmd[cmd_index], (uint8_t)(cmd_len - cmd_index), &param_value);
if (used_param_cmd == 0) {
// 'S' 后没有数字,格式错误
send_response(RESP_TYPE_LEN_ERR, s_report_status_err, sizeof(s_report_status_err));
return;
}
switch (base_cmd)
{
// case 100u:
// // set_pwm(param_value);
// printf("Set PWM to %u\n", param_value);
// return;
default:
break;
}
send_response(RESP_TYPE_TYPE_ERR, s_report_status_err, sizeof(s_report_status_err));
}
}
/**
* @brief 处理串口环形缓冲区中的命令数据,解析完整的协议帧。
* @details 本函数实现一个基于状态机的协议解析器,用于处理格式为 D5 03 LEN [cmd] CRC 的命令帧:
* - 状态1等待包头字节 PROTOCOL_PACKAGE_HEADER (0xD5)
* - 状态2接收板卡类型字节 PROTOCOL_BOARD_TYPE (0x03)
* - 状态3接收长度字段并计算期望的完整帧长度
* - 状态4继续接收剩余数据直到完整帧
* - 状态5对完整帧进行校验包头、板卡类型、CRC并处理
*
* 函数采用非阻塞方式处理,每次调用处理缓冲区中所有可用数据。
* 遇到格式错误、长度异常或校验失败时自动重置状态机。
*
* @note 本函数使用静态变量维护解析状态,因此不可重入。在中断环境中使用需注意并发安全。
* 协议帧最大长度受 PROTOCOL_MAX_FRAME_LEN 限制,超出范围的帧将被丢弃。
*
* @warning 函数依赖 uart_ring_buffer_available() 和 uart_ring_buffer_get()
* 正确实现,若这些函数有缺陷可能导致死循环或数据丢失。
*
* @see handle_command() 用于处理校验通过的完整命令帧
* @see command_sum_crc_calc() 用于计算和校验 CRC 值
* @see send_response() 用于发送错误响应
*
* @ingroup Command
*/
void command_process(void) {
static uint8_t cmd_buf[PROTOCOL_MAX_FRAME_LEN];
static uint8_t cmd_len = 0;
static uint8_t expected_cmd_len = 0; // 0 表示尚未确定总长度
while (uart_ring_buffer_available() > 0) {
int byte = uart_ring_buffer_get();
if (byte < 0) break;
if (cmd_len == 0) {
if ((uint8_t)byte == PROTOCOL_PACKAGE_HEADER) {
cmd_buf[cmd_len++] = (uint8_t)byte;
expected_cmd_len = 0; // 等待进一步字段以确定长度
} else {
// 丢弃非起始字节
}
continue;
}
if (cmd_len >= PROTOCOL_MAX_FRAME_LEN) {
// 防御:缓冲溢出,复位状态机
cmd_len = 0;
expected_cmd_len = 0;
}
// 缓存后续字节
cmd_buf[cmd_len++] = (uint8_t)byte;
// 当到达长度字段(索引 2确定总长度3 + LEN + 1
if (cmd_len == 3) {
uint8_t payload_len = cmd_buf[2];
expected_cmd_len = (uint8_t)(3 + payload_len + 1);
if (expected_cmd_len > PROTOCOL_MAX_FRAME_LEN) {
// 异常:长度超界,复位状态机
cmd_len = 0;
expected_cmd_len = 0;
}
continue;
}
if (expected_cmd_len > 0 && cmd_len == expected_cmd_len) {
// 到帧尾,进行各项校验
bool verification_status = true;
#ifdef DEBUG_VERBOSE
if (cmd_buf[0] != PROTOCOL_PACKAGE_HEADER) {
send_response(RESP_TYPE_HEADER_ERR, s_report_status_err, sizeof(s_report_status_err));
verification_status = false;
}
#endif
if (verification_status && cmd_buf[1] != PROTOCOL_BOARD_TYPE) {
send_response(RESP_TYPE_TYPE_ERR, s_report_status_err, sizeof(s_report_status_err));
verification_status = false;
}
if (verification_status) {
uint8_t crc_calc = command_sum_crc_calc(cmd_buf, expected_cmd_len);
uint8_t crc_recv = cmd_buf[expected_cmd_len - 1];
if (crc_calc != crc_recv) {
send_response(RESP_TYPE_CRC_ERR, s_report_status_err, sizeof(s_report_status_err));
verification_status = false;
}
}
if (verification_status) {
handle_command(cmd_buf, expected_cmd_len);
}
// 复位,等待下一帧
cmd_len = 0;
expected_cmd_len = 0;
}
}
}

109
Src/gd32e23x_it.c Normal file
View File

@@ -0,0 +1,109 @@
/*!
\file gd32e23x_it.c
\brief interrupt service routines
\version 2025-02-10, V2.4.0, demo for GD32E23x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/
#include "gd32e23x_it.h"
#include "systick.h"
#include "uart.h"
#include "uart_ring_buffer.h"
#include "led.h"
/*!
\brief this function handles NMI exception
\param[in] none
\param[out] none
\retval none
*/
void NMI_Handler(void)
{
/* if NMI exception occurs, go to infinite loop */
while(1) {
}
}
/*!
\brief this function handles HardFault exception
\param[in] none
\param[out] none
\retval none
*/
void HardFault_Handler(void)
{
/* if Hard Fault exception occurs, go to infinite loop */
while(1) {
}
}
/*!
\brief this function handles SVC exception
\param[in] none
\param[out] none
\retval none
*/
void SVC_Handler(void)
{
/* if SVC exception occurs, go to infinite loop */
while(1) {
}
}
/*!
\brief this function handles PendSV exception
\param[in] none
\param[out] none
\retval none
*/
void PendSV_Handler(void)
{
/* if PendSV exception occurs, go to infinite loop */
while(1) {
}
}
/*!
\brief this function handles SysTick exception
\param[in] none
\param[out] none
\retval none
*/
void SysTick_Handler(void) {
led_heart_beat(); // LED心跳指示灯
delay_decrement();
}
void USART0_IRQHandler(void) {
if (RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)) {
uint8_t data = usart_data_receive(USART0);
(void)uart_ring_buffer_put(data); // 缓冲满时丢弃,返回值可用于统计
}
}

677
Src/i2c.c Normal file
View File

@@ -0,0 +1,677 @@
//
// 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 Normal file
View File

@@ -0,0 +1,57 @@
#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);
}

83
Src/main.c Normal file
View File

@@ -0,0 +1,83 @@
/*!
\file main.c
\brief running LED
\version 2025-02-10, V2.4.0, demo for GD32E23x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
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 "board_config.h"
/*!
\brief main function
\param[in] none
\param[out] none
\retval none
*/
int main(void)
{
setbuf(stdout, NULL);
systick_config();
rs485_init();
led_init();
#ifdef DEBUG_VERBOSE
char hello_world[] = {"Hello World!\r\n"};
for (uint8_t i = 0; i < sizeof(hello_world); i++)
{
while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {}
usart_data_transmit(RS485_PHY, hello_world[i]);
}
while (usart_flag_get(RS485_PHY, USART_FLAG_TC) == RESET) {}
#endif
i2c_config();
#ifdef DEBUG_VERBOSE
i2c_scan();
i2c_bus_reset();
#endif
while(1){
command_process();
delay_ms(10);
}
}

170
Src/syscalls.c Normal file
View File

@@ -0,0 +1,170 @@
/* Support files for GNU libc. Files in the system namespace go here.
Files in the C namespace (ie those that do not start with an
underscore) go in .c. */
#include <_ansi.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <sys/times.h>
#include <errno.h>
#include <reent.h>
#include <unistd.h>
#include <sys/wait.h>
#include "gd32e23x_usart.h"
#undef errno
extern int errno;
extern int __io_putchar(int ch) __attribute__((weak));
extern int __io_getchar(void) __attribute__((weak));
caddr_t _sbrk(int incr)
{
extern char _end[];
static char *curbrk = _end;
if ((curbrk + incr < _end))
return NULL - 1;
curbrk += incr;
return curbrk - incr;
}
/*
* _gettimeofday primitive (Stub function)
* */
int _gettimeofday (struct timeval * tp, struct timezone * tzp)
{
/* Return fixed data for the timezone. */
if (tzp)
{
tzp->tz_minuteswest = 0;
tzp->tz_dsttime = 0;
}
return 0;
}
void initialise_monitor_handles()
{
}
int _getpid(void)
{
return 1;
}
int _kill(int pid, int sig)
{
errno = EINVAL;
return -1;
}
void _exit (int status)
{
_kill(status, -1);
while (1) {}
}
int _write(int file, char *ptr, int len)
{
int DataIdx;
for (DataIdx = 0; DataIdx < len; DataIdx++)
{
__io_putchar( *ptr++ );
}
return len;
}
int _close(int file)
{
return -1;
}
int _fstat(int file, struct stat *st)
{
st->st_mode = S_IFCHR;
return 0;
}
int _isatty(int file)
{
return 1;
}
int _lseek(int file, int ptr, int dir)
{
return 0;
}
int _read(int file, char *ptr, int len)
{
int DataIdx;
for (DataIdx = 0; DataIdx < len; DataIdx++)
{
*ptr++ = __io_getchar();
}
return len;
}
int _open(char *path, int flags, ...)
{
/* Pretend like we always fail */
return -1;
}
int _wait(int *status)
{
errno = ECHILD;
return -1;
}
int _unlink(char *name)
{
errno = ENOENT;
return -1;
}
int _times(struct tms *buf)
{
return -1;
}
int _stat(char *file, struct stat *st)
{
st->st_mode = S_IFCHR;
return 0;
}
int _link(char *old, char *new)
{
errno = EMLINK;
return -1;
}
int _fork(void)
{
errno = EAGAIN;
return -1;
}
int _execve(char *name, char **argv, char **env)
{
errno = ENOMEM;
return -1;
}
// USART0 printf重定向实现
int __io_putchar(int ch) {
// 等待发送缓冲区空
while (usart_flag_get(USART0, USART_FLAG_TBE) == RESET) {}
usart_data_transmit(USART0, (uint8_t)ch);
return ch;
}

451
Src/system_gd32e23x.c Normal file
View File

@@ -0,0 +1,451 @@
/*!
\file system_gd32e23x.c
\brief CMSIS Cortex-M23 Device Peripheral Access Layer Source File for
GD32E23x Device Series
*/
/* Copyright (c) 2012 ARM LIMITED
Copyright (c) 2025, GigaDevice Semiconductor Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of ARM nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
*
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------*/
/* This file refers the CMSIS standard, some adjustments are made according to GigaDevice chips */
#include "gd32e23x.h"
/* system frequency define */
#define __IRC8M (IRC8M_VALUE) /* internal 8 MHz RC oscillator frequency */
#define __HXTAL (HXTAL_VALUE) /* high speed crystal oscillator frequency */
#define __SYS_OSC_CLK (__IRC8M) /* main oscillator frequency */
#define VECT_TAB_OFFSET (uint32_t)0x00 /* vector table base offset */
/* select a system clock by uncommenting the following line */
//#define __SYSTEM_CLOCK_8M_HXTAL (__HXTAL)
//#define __SYSTEM_CLOCK_8M_IRC8M (__IRC8M)
// #define __SYSTEM_CLOCK_72M_PLL_HXTAL (uint32_t)(72000000)
#define __SYSTEM_CLOCK_72M_PLL_IRC8M_DIV2 (uint32_t)(72000000)
/* The following is to prevent Vcore fluctuations caused by frequency switching.
It is strongly recommended to include it to avoid issues caused by self-removal.
*/
#define RCU_MODIFY(__delay) do{ \
volatile uint32_t i,reg; \
if(0 != __delay){ \
reg = RCU_CFG0; \
reg &= ~(RCU_CFG0_AHBPSC); \
/* CK_AHB = SYSCLK/2 */ \
reg |= RCU_AHB_CKSYS_DIV2; \
RCU_CFG0 = reg; \
for(i=0; i<__delay; i++){ \
} \
reg = RCU_CFG0; \
reg &= ~(RCU_CFG0_AHBPSC); \
reg |= RCU_AHB_CKSYS_DIV4; \
/* CK_AHB = SYSCLK/4 */ \
RCU_CFG0 = reg; \
for(i=0; i<__delay; i++){ \
} \
} \
}while(0)
#define SEL_IRC8M 0x00
#define SEL_HXTAL 0x01
#define SEL_PLL 0x02
/* set the system clock frequency and declare the system clock configuration function */
#ifdef __SYSTEM_CLOCK_8M_HXTAL
uint32_t SystemCoreClock = __SYSTEM_CLOCK_8M_HXTAL;
static void system_clock_8m_hxtal(void);
#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_72M_PLL_HXTAL;
static void system_clock_72m_hxtal(void);
#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M_DIV2)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_72M_PLL_IRC8M_DIV2;
static void system_clock_72m_irc8m(void);
#else
uint32_t SystemCoreClock = __SYSTEM_CLOCK_8M_IRC8M;
static void system_clock_8m_irc8m(void);
#endif /* __SYSTEM_CLOCK_8M_HXTAL */
/* configure the system clock */
static void system_clock_config(void);
/* software delay to prevent the impact of Vcore fluctuations.
It is strongly recommended to include it to avoid issues caused by self-removal. */
static void _soft_delay_(uint32_t time)
{
__IO uint32_t i;
for(i=0; i<time*10; i++){
}
}
/*!
\brief setup the microcontroller system, initialize the system
\param[in] none
\param[out] none
\retval none
*/
void SystemInit (void)
{
/* enable IRC8M */
RCU_CTL0 |= RCU_CTL0_IRC8MEN;
while(0U == (RCU_CTL0 & RCU_CTL0_IRC8MSTB)){
}
if(((RCU_CFG0 & RCU_CFG0_SCSS) == RCU_SCSS_PLL)){
RCU_MODIFY(0x80);
}
RCU_CFG0 &= ~RCU_CFG0_SCS;
_soft_delay_(100);
RCU_CTL0 &= ~(RCU_CTL0_HXTALEN | RCU_CTL0_CKMEN | RCU_CTL0_PLLEN | RCU_CTL0_HXTALBPS);
/* reset RCU */
RCU_CFG0 &= ~(RCU_CFG0_SCS | RCU_CFG0_AHBPSC | RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC |\
RCU_CFG0_ADCPSC | RCU_CFG0_CKOUTSEL | RCU_CFG0_CKOUTDIV | RCU_CFG0_PLLDV);
RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PLLMF | RCU_CFG0_PLLMF4 | RCU_CFG0_PLLDV);
RCU_CFG1 &= ~(RCU_CFG1_PREDV);
RCU_CFG2 &= ~(RCU_CFG2_USART0SEL | RCU_CFG2_ADCSEL);
RCU_CFG2 &= ~RCU_CFG2_IRC28MDIV;
RCU_CFG2 &= ~RCU_CFG2_ADCPSC2;
RCU_CTL1 &= ~RCU_CTL1_IRC28MEN;
RCU_INT = 0x00000000U;
/* configure system clock */
system_clock_config();
#ifdef VECT_TAB_SRAM
nvic_vector_table_set(NVIC_VECTTAB_RAM,VECT_TAB_OFFSET);
#else
nvic_vector_table_set(NVIC_VECTTAB_FLASH,VECT_TAB_OFFSET);
#endif
}
/*!
\brief configure the system clock
\param[in] none
\param[out] none
\retval none
*/
static void system_clock_config(void)
{
#ifdef __SYSTEM_CLOCK_8M_HXTAL
system_clock_8m_hxtal();
#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL)
system_clock_72m_hxtal();
#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M_DIV2)
system_clock_72m_irc8m();
#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC48M_DIV2)
system_clock_72m_irc48m();
#else
system_clock_8m_irc8m();
#endif /* __SYSTEM_CLOCK_8M_HXTAL */
}
#ifdef __SYSTEM_CLOCK_8M_HXTAL
/*!
\brief configure the system clock to 8M by HXTAL
\param[in] none
\param[out] none
\retval none
*/
static void system_clock_8m_hxtal(void)
{
uint32_t timeout = 0U;
uint32_t stab_flag = 0U;
__IO uint32_t reg_temp;
/* enable HXTAL */
RCU_CTL0 |= RCU_CTL0_HXTALEN;
/* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
do{
timeout++;
stab_flag = (RCU_CTL0 & RCU_CTL0_HXTALSTB);
}
while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
/* if fail */
if(0U == (RCU_CTL0 & RCU_CTL0_HXTALSTB)){
while(1){
}
}
/* HXTAL is stable */
/* AHB = SYSCLK */
RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
/* APB2 = AHB */
RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
/* APB1 = AHB */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV1;
reg_temp = RCU_CFG0;
/* select HXTAL as system clock */
reg_temp &= ~RCU_CFG0_SCS;
reg_temp |= RCU_CKSYSSRC_HXTAL;
RCU_CFG0 = reg_temp;
/* wait until HXTAL is selected as system clock */
while(RCU_SCSS_HXTAL != (RCU_CFG0 & RCU_CFG0_SCSS)){
}
}
#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL)
/*!
\brief configure the system clock to 72M by PLL which selects HXTAL as its clock source
\param[in] none
\param[out] none
\retval none
*/
static void system_clock_72m_hxtal(void)
{
uint32_t timeout = 0U;
uint32_t stab_flag = 0U;
__IO uint32_t reg_temp;
/* enable HXTAL */
RCU_CTL0 |= RCU_CTL0_HXTALEN;
/* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
do{
timeout++;
stab_flag = (RCU_CTL0 & RCU_CTL0_HXTALSTB);
}
while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
/* if fail */
if(0U == (RCU_CTL0 & RCU_CTL0_HXTALSTB)){
while(1){
}
}
FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT_2;
/* HXTAL is stable */
/* AHB = SYSCLK */
RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
/* APB2 = AHB */
RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
/* APB1 = AHB */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV1;
/* PLL = HXTAL * 9 = 72 MHz */
RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PLLMF | RCU_CFG0_PLLDV);
RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL9);
/* enable PLL */
RCU_CTL0 |= RCU_CTL0_PLLEN;
/* wait until PLL is stable */
while(0U == (RCU_CTL0 & RCU_CTL0_PLLSTB)){
}
reg_temp = RCU_CFG0;
/* select PLL as system clock */
reg_temp &= ~RCU_CFG0_SCS;
reg_temp |= RCU_CKSYSSRC_PLL;
RCU_CFG0 = reg_temp;
/* wait until PLL is selected as system clock */
while(RCU_SCSS_PLL != (RCU_CFG0 & RCU_CFG0_SCSS)){
}
}
#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M_DIV2)
/*!
\brief configure the system clock to 72M by PLL which selects IRC8M/2 as its clock source
\param[in] none
\param[out] none
\retval none
*/
static void system_clock_72m_irc8m(void)
{
uint32_t timeout = 0U;
uint32_t stab_flag = 0U;
__IO uint32_t reg_temp;
/* enable IRC8M */
RCU_CTL0 |= RCU_CTL0_IRC8MEN;
/* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */
do{
timeout++;
stab_flag = (RCU_CTL0 & RCU_CTL0_IRC8MSTB);
}
while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout));
/* if fail */
if(0U == (RCU_CTL0 & RCU_CTL0_IRC8MSTB)){
while(1){
}
}
FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT_2;
/* AHB = SYSCLK */
RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
/* APB2 = AHB */
RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
/* APB1 = AHB */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV1;
/* PLL = (IRC8M/2) * 18 = 72 MHz */
RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PLLMF);
RCU_CFG0 |= (RCU_PLLSRC_IRC8M_DIV2 | RCU_PLL_MUL18);
/* enable PLL */
RCU_CTL0 |= RCU_CTL0_PLLEN;
/* wait until PLL is stable */
while(0U == (RCU_CTL0 & RCU_CTL0_PLLSTB)){
}
reg_temp = RCU_CFG0;
/* select PLL as system clock */
reg_temp &= ~RCU_CFG0_SCS;
reg_temp |= RCU_CKSYSSRC_PLL;
RCU_CFG0 = reg_temp;
/* wait until PLL is selected as system clock */
while(RCU_SCSS_PLL != (RCU_CFG0 & RCU_CFG0_SCSS)){
}
}
#else
/*!
\brief configure the system clock to 8M by IRC8M
\param[in] none
\param[out] none
\retval none
*/
static void system_clock_8m_irc8m(void)
{
uint32_t timeout = 0U;
uint32_t stab_flag = 0U;
__IO uint32_t reg_temp;
/* enable IRC8M */
RCU_CTL0 |= RCU_CTL0_IRC8MEN;
/* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */
do{
timeout++;
stab_flag = (RCU_CTL0 & RCU_CTL0_IRC8MSTB);
}
while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout));
/* if fail */
if(0U == (RCU_CTL0 & RCU_CTL0_IRC8MSTB)){
while(1){
}
}
/* AHB = SYSCLK */
RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
/* APB2 = AHB */
RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
/* APB1 = AHB */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV1;
reg_temp = RCU_CFG0;
/* select IRC8M as system clock */
reg_temp &= ~RCU_CFG0_SCS;
reg_temp |= RCU_CKSYSSRC_IRC8M;
RCU_CFG0 = reg_temp;
/* wait until IRC8M is selected as system clock */
while(RCU_SCSS_IRC8M != (RCU_CFG0 & RCU_CFG0_SCSS)){
}
}
#endif /* __SYSTEM_CLOCK_8M_HXTAL */
/*!
\brief update the SystemCoreClock with current core clock retrieved from cpu registers
\param[in] none
\param[out] none
\retval none
*/
void SystemCoreClockUpdate (void)
{
uint32_t sws = 0U;
uint32_t pllmf = 0U, pllmf4 = 0U, pllsel = 0U, prediv = 0U, idx = 0U, clk_exp = 0U;
/* exponent of AHB clock divider */
const uint8_t ahb_exp[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
sws = GET_BITS(RCU_CFG0, 2, 3);
switch(sws){
/* IRC8M is selected as CK_SYS */
case SEL_IRC8M:
SystemCoreClock = IRC8M_VALUE;
break;
/* HXTAL is selected as CK_SYS */
case SEL_HXTAL:
SystemCoreClock = HXTAL_VALUE;
break;
/* PLL is selected as CK_SYS */
case SEL_PLL:
/* get the value of PLLMF[3:0] */
pllmf = GET_BITS(RCU_CFG0, 18, 21);
pllmf4 = GET_BITS(RCU_CFG0, 27, 27);
/* high 16 bits */
if(1U == pllmf4){
pllmf += 17U;
}else if(15U == pllmf){
pllmf = 16U;
} else {
pllmf += 2U;
}
/* PLL clock source selection, HXTAL or IRC8M/2 */
pllsel = GET_BITS(RCU_CFG0, 16, 16);
if(0U != pllsel){
prediv = (GET_BITS(RCU_CFG1, 0, 3) + 1U);
SystemCoreClock = (HXTAL_VALUE / prediv) * pllmf;
} else {
SystemCoreClock = (IRC8M_VALUE >> 1) * pllmf;
}
break;
/* IRC8M is selected as CK_SYS */
default:
SystemCoreClock = IRC8M_VALUE;
break;
}
/* calculate AHB clock frequency */
idx = GET_BITS(RCU_CFG0, 4, 7);
clk_exp = ahb_exp[idx];
SystemCoreClock >>= clk_exp;
}
#ifdef __FIRMWARE_VERSION_DEFINE
/*!
\brief get firmware version
\param[in] none
\param[out] none
\retval firmware version
*/
uint32_t gd32e23x_firmware_version_get(void)
{
return __GD32E23x_STDPERIPH_VERSION;
}
#endif /* __FIRMWARE_VERSION_DEFINE */

118
Src/systick.c Normal file
View File

@@ -0,0 +1,118 @@
/**
* ************************************************************************
*
* @file systick.c
* @author GD32
* @brief 通过 SysTick 定时器进行微秒级别和毫秒级别的延时函数
*
* ************************************************************************
* @copyright Copyright (c) 2024 GD32
* ************************************************************************
*/
#include "gd32e23x.h"
#include "systick.h"
volatile static uint32_t delay_count = 0;
/**
* ************************************************************************
* @brief 配置 SysTick 定时器
*
*
* ************************************************************************
*/
void systick_config(void)
{
//设置了 SysTick 定时器的时钟源为 HCLK
systick_clksource_set(SYSTICK_CLKSOURCE_HCLK);
// 配置SysTick为1ms周期中断
// 注意SysTick_Config会自动设置时钟源为HCLK所以需要使用SystemCoreClock/1000
SysTick_Config(SystemCoreClock / 1000U); // 1ms中断
NVIC_SetPriority(SysTick_IRQn, 0x00U);
}
/**
* ************************************************************************
* @brief delay_ms 毫秒延时函数
*
* @param[in] count 毫秒值
*
* ************************************************************************
*/
void delay_10us(uint32_t count)
{
// 基于系统时钟的简单循环延时
// 这是一个粗略的估计,实际延时可能有偏差 实测10.2us
uint32_t loops_per_10us = SystemCoreClock / 1700000; // 粗略估计每10微秒的循环次数
for(uint32_t i = 0; i < count; i++) {
for(volatile uint32_t j = 0; j < loops_per_10us; j++);
}
}
/**
* ************************************************************************
* @brief delay_ms 毫秒延时函数
*
* @param[in] count 毫秒值
*
* ************************************************************************
*/
void delay_ms(uint32_t count)
{
delay_count = count; // 设置延时计数
while (delay_count != 0U);
}
/**
* ************************************************************************
* @brief 每个 SysTick 中断调用时,减少延时计数
*
* @param[in] void
*
* ************************************************************************
*/
void delay_decrement(void)
{
if (delay_count != 0U)
{
delay_count--;
}
}
// /**
// * ************************************************************************
// * @brief delay_ms_safe 毫秒延时函数不干扰SysTick中断
// * @details 使用简单循环实现延时不会重新配置SysTick
// * @param[in] count 毫秒值
// * ************************************************************************
// */
// void delay_ms_safe(uint32_t count)
// {
// // 基于系统时钟的简单循环延时
// // 这是一个粗略的估计,实际延时可能有偏差
// uint32_t loops_per_ms = SystemCoreClock / 14000; // 粗略估计
// for(uint32_t i = 0; i < count; i++) {
// for(volatile uint32_t j = 0; j < loops_per_ms; j++);
// }
// }
// /**
// * ************************************************************************
// * @brief delay_us_safe 微秒延时函数不干扰SysTick中断
// * @details 使用简单循环实现延时不会重新配置SysTick
// * @param[in] count 微秒值
// * ************************************************************************
// */
// void delay_us_safe(uint32_t count)
// {
// // 基于系统时钟的简单循环延时
// // 这是一个粗略的估计,实际延时可能有偏差
// uint32_t loops_per_us = SystemCoreClock / 22000000; // 粗略估计,每微秒的循环次数
// for(uint32_t i = 0; i < count; i++) {
// for(volatile uint32_t j = 0; j < loops_per_us; j++);
// }
// }

72
Src/uart.c Normal file
View File

@@ -0,0 +1,72 @@
#include "uart.h"
#include "gd32e23x_usart.h"
#include "gd32e23x_rcu.h"
#include "gd32e23x_gpio.h"
#include "board_config.h"
void rs485_init(void) {
#ifndef RS485_MAX13487
/* 使能 GPIOA 和 USART0 时钟 */
rcu_periph_clock_enable(RS485_GPIO_RCU);
rcu_periph_clock_enable(RS485_RCU);
/* 配置 PA2 为 USART0_TXPA3 为 USART0_RX */
gpio_af_set(RS485_GPIO_PORT, GPIO_AF_1, RS485_TX_PIN | RS485_RX_PIN | RS485_EN_PIN);
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_50MHZ, RS485_TX_PIN | RS485_RX_PIN);
gpio_mode_set(RS485_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, RS485_EN_PIN);
gpio_output_options_set(RS485_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, RS485_EN_PIN);
/* 配置波特率、数据位、停止位等 */
usart_deinit(RS485_PHY);
usart_word_length_set(RS485_PHY, USART_WL_8BIT);
usart_stop_bit_set(RS485_PHY, USART_STB_1BIT);
usart_parity_config(RS485_PHY, USART_PM_NONE);
usart_baudrate_set(RS485_PHY, RS485_BAUDRATE);
usart_receive_config(RS485_PHY, USART_RECEIVE_ENABLE);
usart_transmit_config(RS485_PHY, USART_TRANSMIT_ENABLE);
usart_driver_assertime_config(RS485_PHY, 0x01);
usart_driver_deassertime_config(RS485_PHY, 0x10);
usart_rs485_driver_enable(RS485_PHY);
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);
#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);
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
}

104
Src/uart_ring_buffer.c Normal file
View File

@@ -0,0 +1,104 @@
/**
* @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;
/**
* @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;
}
/**
* @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);
}
/**
* @brief 从环形缓冲区读取一个字节。
* @details 若缓冲区非空,返回队头字节并推进读指针;若为空,返回 -1。
* @return int 读取到的字节0..255),或 -1 表示缓冲区为空。
* @ingroup RingBuffer
*/
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;
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;
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;
}