feat: 添加GD32E508 DCU应用工程的初始结构

- 新增完整的CMSIS解决方案配置,包含AC6/GCC/IAR/CLANG编译器支持
- 添加项目配置文件,定义了设备层、系统层、应用层的模块结构
- 配置ARM::CMSIS和GigaDevice::GD32E50x_DFP软件包依赖
- 创建多层代码架构:应用入口(main)、系统层(sys)、设备层(device)
- 实现任务管理系统,包含任务调度器和任务执行器分离设计
- 集成ADC、CANFD、定时器、串口、温度采样等核心功能模块
- 添加详细的README文档说明项目结构和代码组织方式
- 配置AC6链接脚本和运行时环境文件
- 设置Git忽略规则以排除编译产物和临时文件
This commit is contained in:
cbb
2026-05-10 17:28:14 +08:00
commit 4b8b47bc9b
116 changed files with 36659 additions and 0 deletions
+126
View File
@@ -0,0 +1,126 @@
/**
* @file ADC.cpp
* @brief ADC 采样模块实现。
*/
#include "main.h"
#include "device/adc.h"
#include "sys/systick.h"
#include "sys/config.h"
namespace {
constexpr uint32_t kDmaPeriph = DMA0;
constexpr dma_channel_enum kDmaChannel = DMA_CH0;
constexpr uint32_t kChannelCount = 3U;
constexpr uint32_t kVinGpio = GPIOA;
constexpr uint32_t kVinPin = GPIO_PIN_6;
volatile uint16_t s_dmaBuffer[kChannelCount] = {0U, 0U, 0U};
volatile uint8_t s_dmaReady = 0U;
} // namespace
void Adc::initDma()
{
dma_parameter_struct dma_initpara{};
rcu_periph_clock_enable(RCU_DMA0);
dma_deinit(kDmaPeriph, kDmaChannel);
dma_struct_para_init(&dma_initpara);
dma_initpara.periph_addr = (uint32_t)(&ADC_RDATA(CONFIG_ADC_INSTANCE));
dma_initpara.memory_addr = (uint32_t)s_dmaBuffer;
dma_initpara.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_initpara.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
dma_initpara.memory_width = DMA_MEMORY_WIDTH_16BIT;
dma_initpara.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_initpara.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_initpara.number = kChannelCount;
dma_initpara.priority = DMA_PRIORITY_HIGH;
dma_init(kDmaPeriph, kDmaChannel, &dma_initpara);
dma_circulation_enable(kDmaPeriph, kDmaChannel);
dma_channel_enable(kDmaPeriph, kDmaChannel);
}
void Adc::init()
{
rcu_periph_clock_enable(RCU_ADC0);
rcu_periph_clock_enable(RCU_GPIOA);
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8);
/* 原理图:PA6 -> VIN_ADC,为外部电源分压检测输入 */
gpio_init(kVinGpio, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, kVinPin);
initDma();
adc_deinit(CONFIG_ADC_INSTANCE);
adc_mode_config(ADC_MODE_FREE);
adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_SCAN_MODE, ENABLE);
adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_CONTINUOUS_MODE, ENABLE);
adc_data_alignment_config(CONFIG_ADC_INSTANCE, ADC_DATAALIGN_RIGHT);
adc_channel_length_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, kChannelCount);
adc_regular_channel_config(CONFIG_ADC_INSTANCE, 0U, CONFIG_ADC_VIN_CHANNEL, CONFIG_ADC_SAMPLE_TIME);
adc_regular_channel_config(CONFIG_ADC_INSTANCE, 1U, CONFIG_ADC_TEMP_CHANNEL, CONFIG_ADC_SAMPLE_TIME);
adc_regular_channel_config(CONFIG_ADC_INSTANCE, 2U, CONFIG_ADC_VREFINT_CHANNEL, CONFIG_ADC_SAMPLE_TIME);
adc_external_trigger_source_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
adc_external_trigger_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, DISABLE);
adc_dma_mode_enable(CONFIG_ADC_INSTANCE);
adc_tempsensor_vrefint_enable();
SysTickTimer::delayMs(CONFIG_ADC_STABILIZE_DELAY_MS);
adc_enable(CONFIG_ADC_INSTANCE);
SysTickTimer::delayMs(CONFIG_ADC_STABILIZE_DELAY_MS);
adc_calibration_enable(CONFIG_ADC_INSTANCE);
adc_software_trigger_enable(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL);
SysTickTimer::delayMs(1U);
s_dmaReady = 1U;
}
bool Adc::dmaReady()
{
return s_dmaReady != 0U;
}
const uint16_t *Adc::dmaBuffer()
{
return (const uint16_t *)s_dmaBuffer;
}
uint16_t Adc::readRaw(uint8_t channel)
{
if(channel == CONFIG_ADC_VIN_CHANNEL) return s_dmaBuffer[0];
if(channel == CONFIG_ADC_TEMP_CHANNEL) return s_dmaBuffer[1];
if(channel == CONFIG_ADC_VREFINT_CHANNEL) return s_dmaBuffer[2];
return 0U;
}
uint16_t Adc::readVinRaw() { return readRaw(CONFIG_ADC_VIN_CHANNEL); }
uint16_t Adc::readVrefRaw() { return readRaw(CONFIG_ADC_VREFINT_CHANNEL); }
uint16_t Adc::readTempRaw() { return readRaw(CONFIG_ADC_TEMP_CHANNEL); }
float Adc::calculateVinAdcVoltage(uint16_t vinRaw, float vdda)
{
return (static_cast<float>(vinRaw) * vdda) / CONFIG_ADC_MAX_VALUE;
}
float Adc::calculateVdda(uint16_t vrefRaw)
{
if(vrefRaw == 0U) return CONFIG_ADC_DEFAULT_VDDA;
return (CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE * CONFIG_ADC_MAX_VALUE) / static_cast<float>(vrefRaw);
}
float Adc::calculateTemperature(uint16_t tempRaw, float vdda)
{
const float sensorVoltage = (static_cast<float>(tempRaw) * vdda) / CONFIG_ADC_MAX_VALUE;
return ((CONFIG_ADC_TEMP_V25 - sensorVoltage) / CONFIG_ADC_TEMP_AVG_SLOPE) + 25.0f;
}
void Adc::collectSample(Sample *sample)
{
if(sample == nullptr) return;
sample->vin_raw = readVinRaw();
sample->temp_raw = readTempRaw();
sample->vref_raw = readVrefRaw();
sample->vdda = calculateVdda(sample->vref_raw);
sample->vin_adc_v = calculateVinAdcVoltage(sample->vin_raw, sample->vdda);
sample->temperature = calculateTemperature(sample->temp_raw, sample->vdda);
}
+126
View File
@@ -0,0 +1,126 @@
#include "main.h"
#include "device/adc.h"
#include "sys/systick.h"
#define TEMP_ADC ADC0
#define TEMP_ADC_CHANNEL ADC_CHANNEL_16
#define VREFINT_ADC_CHANNEL ADC_CHANNEL_17
#define ADC_MAX_VALUE 4095.0f
#define VREFINT_TYPICAL_VOLTAGE 1.20f
#define TEMP_V25 1.43f
#define TEMP_AVG_SLOPE 0.0043f
/**
* @brief 初始化 ADC 硬件及内部温度/VREF 通道。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 完成 ADC 时钟、模式、触发方式、内部通道和校准流程配置。
*/
void adc_hw_init(void)
{
rcu_periph_clock_enable(RCU_ADC0);
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8);
adc_deinit(TEMP_ADC);
adc_mode_config(ADC_MODE_FREE);
adc_special_function_config(TEMP_ADC, ADC_SCAN_MODE, DISABLE);
adc_special_function_config(TEMP_ADC, ADC_CONTINUOUS_MODE, DISABLE);
adc_data_alignment_config(TEMP_ADC, ADC_DATAALIGN_RIGHT);
adc_channel_length_config(TEMP_ADC, ADC_REGULAR_CHANNEL, 1U);
adc_external_trigger_source_config(TEMP_ADC, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
adc_external_trigger_config(TEMP_ADC, ADC_REGULAR_CHANNEL, DISABLE);
adc_tempsensor_vrefint_enable();
delay_ms(2U);
adc_enable(TEMP_ADC);
delay_ms(2U);
adc_calibration_enable(TEMP_ADC);
}
/**
* @brief 执行指定通道的一次 ADC 单次采样。
* @param[in] channel 待采样的 ADC 通道号。
* @param[out] 无。
* @retval uint16_t 指定通道的原始 ADC 采样值。
* @note 函数采用阻塞式等待,直到 EOC 置位后返回。
*/
uint16_t adc_single_read(uint8_t channel)
{
adc_regular_channel_config(TEMP_ADC, 0U, channel, ADC_SAMPLETIME_239POINT5);
adc_flag_clear(TEMP_ADC, ADC_FLAG_EOC);
adc_software_trigger_enable(TEMP_ADC, ADC_REGULAR_CHANNEL);
while(RESET == adc_flag_get(TEMP_ADC, ADC_FLAG_EOC)) {
}
return adc_regular_data_read(TEMP_ADC);
}
/**
* @brief 读取内部参考电压通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部参考电压通道的原始 ADC 值。
*/
uint16_t adc_read_vref_raw(void)
{
return adc_single_read(VREFINT_ADC_CHANNEL);
}
/**
* @brief 读取内部温度通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部温度通道的原始 ADC 值。
*/
uint16_t adc_read_temp_raw(void)
{
return adc_single_read(TEMP_ADC_CHANNEL);
}
/**
* @brief 根据内部参考电压原始值计算实际 VDDA。
* @param[in] vref_raw 内部参考电压通道原始 ADC 数值。
* @param[out] 无。
* @retval float 换算得到的 VDDA 电压值,单位 V。
* @note 当 vref_raw 为 0 时返回默认 3.3V,避免出现除零异常。
*/
float adc_calculate_vdda(uint16_t vref_raw)
{
if(vref_raw == 0U) {
return 3.3f;
}
return (VREFINT_TYPICAL_VOLTAGE * ADC_MAX_VALUE) / static_cast<float>(vref_raw);
}
/**
* @brief 根据温度原始值与 VDDA 计算芯片温度。
* @param[in] temp_raw 内部温度通道原始 ADC 数值。
* @param[in] vdda 当前供电电压,单位 V。
* @param[out] 无。
* @retval float 换算得到的芯片温度,单位 ℃。
*/
float adc_calculate_temperature(uint16_t temp_raw, float vdda)
{
const float sensor_voltage = (static_cast<float>(temp_raw) * vdda) / ADC_MAX_VALUE;
return ((TEMP_V25 - sensor_voltage) / TEMP_AVG_SLOPE) + 25.0f;
}
/**
* @brief 采集一组完整 ADC 样本并完成工程值换算。
* @param[in] sample 指向输出样本结构体的指针;允许为空。
* @param[out] sample 当指针非空时,写入温度/VREF 原始值与换算结果。
* @retval 无返回值。
* @note 当 sample 为 NULL 时函数直接返回,不执行任何数据写入。
*/
void adc_collect_sample(adc_sample_t *sample)
{
if(sample == nullptr) {
return;
}
sample->vref_raw = adc_read_vref_raw();
sample->temp_raw = adc_read_temp_raw();
sample->vdda = adc_calculate_vdda(sample->vref_raw);
sample->temperature = adc_calculate_temperature(sample->temp_raw, sample->vdda);
}
+119
View File
@@ -0,0 +1,119 @@
#include "main.h"
#include "device/adc.h"
#include "sys/systick.h"
#include "sys/config.h"
/**
* @brief 初始化 ADC 硬件及内部温度/VREF 通道。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 完成 ADC 时钟、模式、触发方式、内部通道和校准流程配置。
*/
void adc_hw_init(void)
{
rcu_periph_clock_enable(RCU_ADC0);
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8);
adc_deinit(CONFIG_ADC_INSTANCE);
adc_mode_config(ADC_MODE_FREE);
adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_SCAN_MODE, DISABLE);
adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_CONTINUOUS_MODE, DISABLE);
adc_data_alignment_config(CONFIG_ADC_INSTANCE, ADC_DATAALIGN_RIGHT);
adc_channel_length_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, 1U);
adc_external_trigger_source_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
adc_external_trigger_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, DISABLE);
adc_tempsensor_vrefint_enable();
delay_ms(CONFIG_ADC_STABILIZE_DELAY_MS);
adc_enable(CONFIG_ADC_INSTANCE);
delay_ms(CONFIG_ADC_STABILIZE_DELAY_MS);
adc_calibration_enable(CONFIG_ADC_INSTANCE);
}
/**
* @brief 执行指定通道的一次 ADC 单次采样。
* @param[in] channel 待采样的 ADC 通道号。
* @param[out] 无。
* @retval uint16_t 指定通道的原始 ADC 采样值。
* @note 函数采用阻塞式等待,直到 EOC 置位后返回。
*/
uint16_t adc_single_read(uint8_t channel)
{
adc_regular_channel_config(CONFIG_ADC_INSTANCE, 0U, channel, CONFIG_ADC_SAMPLE_TIME);
adc_flag_clear(CONFIG_ADC_INSTANCE, ADC_FLAG_EOC);
adc_software_trigger_enable(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL);
while(RESET == adc_flag_get(CONFIG_ADC_INSTANCE, ADC_FLAG_EOC)) {
}
return adc_regular_data_read(CONFIG_ADC_INSTANCE);
}
/**
* @brief 读取内部参考电压通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部参考电压通道的原始 ADC 值。
*/
uint16_t adc_read_vref_raw(void)
{
return adc_single_read(CONFIG_ADC_VREFINT_CHANNEL);
}
/**
* @brief 读取内部温度通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部温度通道的原始 ADC 值。
*/
uint16_t adc_read_temp_raw(void)
{
return adc_single_read(CONFIG_ADC_TEMP_CHANNEL);
}
/**
* @brief 根据内部参考电压原始值计算实际 VDDA。
* @param[in] vref_raw 内部参考电压通道原始 ADC 数值。
* @param[out] 无。
* @retval float 换算得到的 VDDA 电压值,单位 V。
* @note 当 vref_raw 为 0 时返回默认 3.3V,避免出现除零异常。
*/
float adc_calculate_vdda(uint16_t vref_raw)
{
if(vref_raw == 0U) {
return CONFIG_ADC_DEFAULT_VDDA;
}
return (CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE * CONFIG_ADC_MAX_VALUE) / static_cast<float>(vref_raw);
}
/**
* @brief 根据温度原始值与 VDDA 计算芯片温度。
* @param[in] temp_raw 内部温度通道原始 ADC 数值。
* @param[in] vdda 当前供电电压,单位 V。
* @param[out] 无。
* @retval float 换算得到的芯片温度,单位 ℃。
*/
float adc_calculate_temperature(uint16_t temp_raw, float vdda)
{
const float sensor_voltage = (static_cast<float>(temp_raw) * vdda) / CONFIG_ADC_MAX_VALUE;
return ((CONFIG_ADC_TEMP_V25 - sensor_voltage) / CONFIG_ADC_TEMP_AVG_SLOPE) + 25.0f;
}
/**
* @brief 采集一组完整 ADC 样本并完成工程值换算。
* @param[in] sample 指向输出样本结构体的指针;允许为空。
* @param[out] sample 当指针非空时,写入温度/VREF 原始值与换算结果。
* @retval 无返回值。
* @note 当 sample 为 NULL 时函数直接返回,不执行任何数据写入。
*/
void adc_collect_sample(adc_sample_t *sample)
{
if(sample == nullptr) {
return;
}
sample->vref_raw = adc_read_vref_raw();
sample->temp_raw = adc_read_temp_raw();
sample->vdda = adc_calculate_vdda(sample->vref_raw);
sample->temperature = adc_calculate_temperature(sample->temp_raw, sample->vdda);
}
+218
View File
@@ -0,0 +1,218 @@
#include "main.h"
#include "device/adc.h"
#include "sys/systick.h"
#include "sys/config.h"
namespace {
constexpr uint32_t kAdcDmaPeriph = DMA0;
constexpr dma_channel_enum kAdcDmaChannel = DMA_CH0;
constexpr uint32_t kAdcChannelCount = 3U;
constexpr uint32_t kVinAdcGpio = GPIOA;
constexpr uint32_t kVinAdcPin = GPIO_PIN_6;
volatile uint16_t g_adc_dma_buffer[kAdcChannelCount] = {0U, 0U, 0U};
volatile uint8_t g_adc_dma_ready = 0U;
/**
* @brief 配置 ADC 原始数据到内存的 DMA 循环搬运。
*/
void adc_dma_init(void)
{
dma_parameter_struct dma_initpara{};
rcu_periph_clock_enable(RCU_DMA0);
dma_deinit(kAdcDmaPeriph, kAdcDmaChannel);
dma_struct_para_init(&dma_initpara);
dma_initpara.periph_addr = (uint32_t)(&ADC_RDATA(CONFIG_ADC_INSTANCE));
dma_initpara.memory_addr = (uint32_t)g_adc_dma_buffer;
dma_initpara.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_initpara.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
dma_initpara.memory_width = DMA_MEMORY_WIDTH_16BIT;
dma_initpara.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_initpara.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_initpara.number = kAdcChannelCount;
dma_initpara.priority = DMA_PRIORITY_HIGH;
dma_init(kAdcDmaPeriph, kAdcDmaChannel, &dma_initpara);
dma_circulation_enable(kAdcDmaPeriph, kAdcDmaChannel);
dma_channel_enable(kAdcDmaPeriph, kAdcDmaChannel);
}
} // namespace
/**
* @brief 初始化 ADC 硬件及内部温度/VREF/VIN_ADC 通道。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 完成 ADC 三通道扫描、DMA 循环搬运、PA6(VIN_ADC) 模拟输入配置以及内部通道校准。
*/
void adc_hw_init(void)
{
rcu_periph_clock_enable(RCU_ADC0);
rcu_periph_clock_enable(RCU_GPIOA);
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8);
/* 原理图:PA6 -> VIN_ADC,为外部电源分压检测输入 */
gpio_init(kVinAdcGpio, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, kVinAdcPin);
adc_dma_init();
adc_deinit(CONFIG_ADC_INSTANCE);
adc_mode_config(ADC_MODE_FREE);
adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_SCAN_MODE, ENABLE);
adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_CONTINUOUS_MODE, ENABLE);
adc_data_alignment_config(CONFIG_ADC_INSTANCE, ADC_DATAALIGN_RIGHT);
adc_channel_length_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, kAdcChannelCount);
adc_regular_channel_config(CONFIG_ADC_INSTANCE, 0U, CONFIG_ADC_VIN_CHANNEL, CONFIG_ADC_SAMPLE_TIME);
adc_regular_channel_config(CONFIG_ADC_INSTANCE, 1U, CONFIG_ADC_TEMP_CHANNEL, CONFIG_ADC_SAMPLE_TIME);
adc_regular_channel_config(CONFIG_ADC_INSTANCE, 2U, CONFIG_ADC_VREFINT_CHANNEL, CONFIG_ADC_SAMPLE_TIME);
adc_external_trigger_source_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
adc_external_trigger_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, DISABLE);
adc_dma_mode_enable(CONFIG_ADC_INSTANCE);
adc_tempsensor_vrefint_enable();
delay_ms(CONFIG_ADC_STABILIZE_DELAY_MS);
adc_enable(CONFIG_ADC_INSTANCE);
delay_ms(CONFIG_ADC_STABILIZE_DELAY_MS);
adc_calibration_enable(CONFIG_ADC_INSTANCE);
adc_software_trigger_enable(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL);
delay_ms(1U);
g_adc_dma_ready = 1U;
}
/**
* @brief 查询 ADC DMA 循环缓存是否已完成首次有效填充。
* @param[in] 无。
* @param[out] 无。
* @retval uint8_t 返回非 0 表示缓存已可用,返回 0 表示仍未准备好。
*/
uint8_t adc_dma_ready(void)
{
return g_adc_dma_ready;
}
/**
* @brief 获取 ADC DMA 原始循环缓存只读指针。
* @param[in] 无。
* @param[out] 无。
* @retval const uint16_t* 返回长度为 3 的原始缓存指针,顺序为 VIN_ADC、温度通道、VREF 通道。
*/
const uint16_t *adc_dma_buffer_get(void)
{
return (const uint16_t *)g_adc_dma_buffer;
}
/**
* @brief 执行指定通道的一次 ADC 单次采样。
* @param[in] channel 待采样的 ADC 通道号。
* @param[out] 无。
* @retval uint16_t 指定通道的原始 ADC 采样值。
* @note 在 DMA 缓存模式下,该函数优先返回已缓存的对应通道原始值;若通道不受支持则返回 0。
*/
uint16_t adc_single_read(uint8_t channel)
{
if(channel == CONFIG_ADC_VIN_CHANNEL) {
return g_adc_dma_buffer[0];
}
if(channel == CONFIG_ADC_TEMP_CHANNEL) {
return g_adc_dma_buffer[1];
}
if(channel == CONFIG_ADC_VREFINT_CHANNEL) {
return g_adc_dma_buffer[2];
}
return 0U;
}
/**
* @brief 读取外部 VIN_ADC(PA6)通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 外部 VIN_ADC 通道的原始 ADC 值。
*/
uint16_t adc_read_vin_raw(void)
{
return adc_single_read(CONFIG_ADC_VIN_CHANNEL);
}
/**
* @brief 读取内部参考电压通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部参考电压通道的原始 ADC 值。
*/
uint16_t adc_read_vref_raw(void)
{
return adc_single_read(CONFIG_ADC_VREFINT_CHANNEL);
}
/**
* @brief 读取内部温度通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部温度通道的原始 ADC 值。
*/
uint16_t adc_read_temp_raw(void)
{
return adc_single_read(CONFIG_ADC_TEMP_CHANNEL);
}
/**
* @brief 根据外部 VIN_ADC 原始值与 VDDA 计算 ADC 引脚处电压。
* @param[in] vin_raw 外部 VIN_ADC 通道原始 ADC 数值。
* @param[in] vdda 当前供电电压,单位 V。
* @param[out] 无。
* @retval float VIN_ADC 引脚电压,单位 V。
* @note 当前仅计算分压后的 ADC 端电压;若需换算输入母线电压,需要补充完整分压比。
*/
float adc_calculate_vin_adc_voltage(uint16_t vin_raw, float vdda)
{
return (static_cast<float>(vin_raw) * vdda) / CONFIG_ADC_MAX_VALUE;
}
/**
* @brief 根据内部参考电压原始值计算实际 VDDA。
* @param[in] vref_raw 内部参考电压通道原始 ADC 数值。
* @param[out] 无。
* @retval float 换算得到的 VDDA 电压值,单位 V。
* @note 当 vref_raw 为 0 时返回默认 3.3V,避免出现除零异常。
*/
float adc_calculate_vdda(uint16_t vref_raw)
{
if(vref_raw == 0U) {
return CONFIG_ADC_DEFAULT_VDDA;
}
return (CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE * CONFIG_ADC_MAX_VALUE) / static_cast<float>(vref_raw);
}
/**
* @brief 根据温度原始值与 VDDA 计算芯片温度。
* @param[in] temp_raw 内部温度通道原始 ADC 数值。
* @param[in] vdda 当前供电电压,单位 V。
* @param[out] 无。
* @retval float 换算得到的芯片温度,单位 ℃。
*/
float adc_calculate_temperature(uint16_t temp_raw, float vdda)
{
const float sensor_voltage = (static_cast<float>(temp_raw) * vdda) / CONFIG_ADC_MAX_VALUE;
return ((CONFIG_ADC_TEMP_V25 - sensor_voltage) / CONFIG_ADC_TEMP_AVG_SLOPE) + 25.0f;
}
/**
* @brief 采集一组完整 ADC 样本并完成工程值换算。
* @param[in] sample 指向输出样本结构体的指针;允许为空。
* @param[out] sample 当指针非空时,写入 VIN/温度/VREF 原始值与换算结果。
* @retval 无返回值。
* @note 当 sample 为 NULL 时函数直接返回,不执行任何数据写入。
*/
void adc_collect_sample(adc_sample_t *sample)
{
if(sample == nullptr) {
return;
}
sample->vin_raw = adc_read_vin_raw();
sample->temp_raw = adc_read_temp_raw();
sample->vref_raw = adc_read_vref_raw();
sample->vdda = adc_calculate_vdda(sample->vref_raw);
sample->vin_adc_v = adc_calculate_vin_adc_voltage(sample->vin_raw, sample->vdda);
sample->temperature = adc_calculate_temperature(sample->temp_raw, sample->vdda);
}
+182
View File
@@ -0,0 +1,182 @@
#include "main.h"
#include "device/adc.h"
#include "sys/systick.h"
#include "sys/config.h"
namespace {
constexpr uint32_t kAdcDmaPeriph = DMA0;
constexpr dma_channel_enum kAdcDmaChannel = DMA_CH0;
constexpr uint32_t kAdcChannelCount = 2U;
volatile uint16_t g_adc_dma_buffer[kAdcChannelCount] = {0U, 0U};
volatile uint8_t g_adc_dma_ready = 0U;
/**
* @brief 配置 ADC 原始数据到内存的 DMA 循环搬运。
*/
void adc_dma_init(void)
{
dma_parameter_struct dma_initpara{};
rcu_periph_clock_enable(RCU_DMA0);
dma_deinit(kAdcDmaPeriph, kAdcDmaChannel);
dma_struct_para_init(&dma_initpara);
dma_initpara.periph_addr = (uint32_t)(&ADC_RDATA(CONFIG_ADC_INSTANCE));
dma_initpara.memory_addr = (uint32_t)g_adc_dma_buffer;
dma_initpara.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_initpara.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
dma_initpara.memory_width = DMA_MEMORY_WIDTH_16BIT;
dma_initpara.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_initpara.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_initpara.number = kAdcChannelCount;
dma_initpara.priority = DMA_PRIORITY_HIGH;
dma_init(kAdcDmaPeriph, kAdcDmaChannel, &dma_initpara);
dma_circulation_enable(kAdcDmaPeriph, kAdcDmaChannel);
dma_channel_enable(kAdcDmaPeriph, kAdcDmaChannel);
}
} // namespace
/**
* @brief 初始化 ADC 硬件及内部温度/VREF 通道。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 完成 ADC 双通道扫描、DMA 循环搬运、内部通道使能与校准流程配置。
*/
void adc_hw_init(void)
{
rcu_periph_clock_enable(RCU_ADC0);
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8);
adc_dma_init();
adc_deinit(CONFIG_ADC_INSTANCE);
adc_mode_config(ADC_MODE_FREE);
adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_SCAN_MODE, ENABLE);
adc_special_function_config(CONFIG_ADC_INSTANCE, ADC_CONTINUOUS_MODE, ENABLE);
adc_data_alignment_config(CONFIG_ADC_INSTANCE, ADC_DATAALIGN_RIGHT);
adc_channel_length_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, kAdcChannelCount);
adc_regular_channel_config(CONFIG_ADC_INSTANCE, 0U, CONFIG_ADC_TEMP_CHANNEL, CONFIG_ADC_SAMPLE_TIME);
adc_regular_channel_config(CONFIG_ADC_INSTANCE, 1U, CONFIG_ADC_VREFINT_CHANNEL, CONFIG_ADC_SAMPLE_TIME);
adc_external_trigger_source_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
adc_external_trigger_config(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL, DISABLE);
adc_dma_mode_enable(CONFIG_ADC_INSTANCE);
adc_tempsensor_vrefint_enable();
delay_ms(CONFIG_ADC_STABILIZE_DELAY_MS);
adc_enable(CONFIG_ADC_INSTANCE);
delay_ms(CONFIG_ADC_STABILIZE_DELAY_MS);
adc_calibration_enable(CONFIG_ADC_INSTANCE);
adc_software_trigger_enable(CONFIG_ADC_INSTANCE, ADC_REGULAR_CHANNEL);
delay_ms(1U);
g_adc_dma_ready = 1U;
}
/**
* @brief 查询 ADC DMA 循环缓存是否已完成首次有效填充。
* @param[in] 无。
* @param[out] 无。
* @retval uint8_t 返回非 0 表示缓存已可用,返回 0 表示仍未准备好。
*/
uint8_t adc_dma_ready(void)
{
return g_adc_dma_ready;
}
/**
* @brief 获取 ADC DMA 原始循环缓存只读指针。
* @param[in] 无。
* @param[out] 无。
* @retval const uint16_t* 返回长度为 2 的原始缓存指针,顺序为温度通道、VREF 通道。
*/
const uint16_t *adc_dma_buffer_get(void)
{
return (const uint16_t *)g_adc_dma_buffer;
}
/**
* @brief 执行指定通道的一次 ADC 单次采样。
* @param[in] channel 待采样的 ADC 通道号。
* @param[out] 无。
* @retval uint16_t 指定通道的原始 ADC 采样值。
* @note 在 DMA 缓存模式下,该函数优先返回已缓存的对应通道原始值;若通道不受支持则返回 0。
*/
uint16_t adc_single_read(uint8_t channel)
{
if(channel == CONFIG_ADC_TEMP_CHANNEL) {
return g_adc_dma_buffer[0];
}
if(channel == CONFIG_ADC_VREFINT_CHANNEL) {
return g_adc_dma_buffer[1];
}
return 0U;
}
/**
* @brief 读取内部参考电压通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部参考电压通道的原始 ADC 值。
*/
uint16_t adc_read_vref_raw(void)
{
return adc_single_read(CONFIG_ADC_VREFINT_CHANNEL);
}
/**
* @brief 读取内部温度通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部温度通道的原始 ADC 值。
*/
uint16_t adc_read_temp_raw(void)
{
return adc_single_read(CONFIG_ADC_TEMP_CHANNEL);
}
/**
* @brief 根据内部参考电压原始值计算实际 VDDA。
* @param[in] vref_raw 内部参考电压通道原始 ADC 数值。
* @param[out] 无。
* @retval float 换算得到的 VDDA 电压值,单位 V。
* @note 当 vref_raw 为 0 时返回默认 3.3V,避免出现除零异常。
*/
float adc_calculate_vdda(uint16_t vref_raw)
{
if(vref_raw == 0U) {
return CONFIG_ADC_DEFAULT_VDDA;
}
return (CONFIG_ADC_VREFINT_TYPICAL_VOLTAGE * CONFIG_ADC_MAX_VALUE) / static_cast<float>(vref_raw);
}
/**
* @brief 根据温度原始值与 VDDA 计算芯片温度。
* @param[in] temp_raw 内部温度通道原始 ADC 数值。
* @param[in] vdda 当前供电电压,单位 V。
* @param[out] 无。
* @retval float 换算得到的芯片温度,单位 ℃。
*/
float adc_calculate_temperature(uint16_t temp_raw, float vdda)
{
const float sensor_voltage = (static_cast<float>(temp_raw) * vdda) / CONFIG_ADC_MAX_VALUE;
return ((CONFIG_ADC_TEMP_V25 - sensor_voltage) / CONFIG_ADC_TEMP_AVG_SLOPE) + 25.0f;
}
/**
* @brief 采集一组完整 ADC 样本并完成工程值换算。
* @param[in] sample 指向输出样本结构体的指针;允许为空。
* @param[out] sample 当指针非空时,写入温度/VREF 原始值与换算结果。
* @retval 无返回值。
* @note 当 sample 为 NULL 时函数直接返回,不执行任何数据写入。
*/
void adc_collect_sample(adc_sample_t *sample)
{
if(sample == nullptr) {
return;
}
sample->temp_raw = adc_read_temp_raw();
sample->vref_raw = adc_read_vref_raw();
sample->vdda = adc_calculate_vdda(sample->vref_raw);
sample->temperature = adc_calculate_temperature(sample->temp_raw, sample->vdda);
}
+232
View File
@@ -0,0 +1,232 @@
/**
* @file CANFD.cpp
* @brief CANFD 通信模块实现。
*/
#include "main.h"
#include "device/canfd.h"
#include "sys/config.h"
namespace {
constexpr uint32_t kSlpGpio = GPIOA;
constexpr uint32_t kSlpPin = GPIO_PIN_8;
struct CanFdTxBuffer {
can_trasnmit_message_struct tx_message;
uint8_t payload[64];
uint8_t valid;
};
CanFdTxBuffer s_txBuffer;
} // namespace
CanFd::Diag CanFd::s_diag;
CanFd::RxFrame CanFd::s_rxFrame;
void CanFd::initGpio()
{
rcu_periph_clock_enable(CONFIG_CAN_RCU);
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_11);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);
gpio_init(kSlpGpio, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, kSlpPin);
}
void CanFd::filterAcceptAll()
{
can_filter_parameter_struct can_filter{};
can_filter.filter_number = 0U;
can_filter.filter_mode = CAN_FILTERMODE_MASK;
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
can_filter.filter_list_high = 0x0000U;
can_filter.filter_list_low = 0x0000U;
can_filter.filter_mask_high = 0x0000U;
can_filter.filter_mask_low = 0x0000U;
can_filter.filter_fifo_number = CAN_FIFO0;
can_filter.filter_enable = ENABLE;
can_filter_init(CONFIG_CAN_INSTANCE, &can_filter);
}
void CanFd::initNominalTiming()
{
can_parameter_struct can_parameter{};
can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
can_parameter.time_triggered = DISABLE;
can_parameter.auto_bus_off_recovery = ENABLE;
can_parameter.auto_wake_up = DISABLE;
can_parameter.auto_retrans = ENABLE;
can_parameter.rec_fifo_overwrite = ENABLE;
can_parameter.trans_fifo_order = ENABLE;
can_parameter.working_mode = CAN_NORMAL_MODE;
can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
can_parameter.time_segment_1 = CAN_BT_BS1_7TQ;
can_parameter.time_segment_2 = CAN_BT_BS2_2TQ;
can_parameter.prescaler = 6U;
can_init(CONFIG_CAN_INSTANCE, &can_parameter);
}
void CanFd::initFdMode()
{
#ifdef GD32E508
can_fdframe_struct can_fd_parameter{};
can_fd_tdc_struct can_fd_tdc_parameter{};
can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter);
can_fd_parameter.fd_frame = ENABLE;
can_fd_parameter.excp_event_detect = ENABLE;
can_fd_parameter.delay_compensation = ENABLE;
can_fd_tdc_parameter.tdc_filter = 0x04U;
can_fd_tdc_parameter.tdc_mode = CAN_TDCMOD_CALC_AND_OFFSET;
can_fd_tdc_parameter.tdc_offset = 0x04U;
can_fd_parameter.p_delay_compensation = &can_fd_tdc_parameter;
can_fd_parameter.iso_bosch = CAN_FDMOD_ISO;
can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE;
can_fd_parameter.data_prescaler = 6U;
can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ;
can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ;
can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ;
can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter);
#else
can_fd_function_enable(CONFIG_CAN_INSTANCE);
#endif
}
void CanFd::buildStatusFrame(const StatusPayload *status)
{
const int16_t temp_x100 = static_cast<int16_t>(status->temperature * 100.0f);
const uint16_t vdda_x1000 = static_cast<uint16_t>(status->vdda * 1000.0f);
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &s_txBuffer.tx_message);
s_txBuffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID;
s_txBuffer.tx_message.tx_efid = 0U;
s_txBuffer.tx_message.tx_ff = CAN_FF_STANDARD;
s_txBuffer.tx_message.tx_ft = CAN_FT_DATA;
s_txBuffer.tx_message.tx_dlen = CANFD_STATUS_DLEN;
#ifdef GD32E508
s_txBuffer.tx_message.fd_flag = ENABLE;
s_txBuffer.tx_message.fd_brs = ENABLE;
s_txBuffer.tx_message.fd_esi = CAN_ESI_DOMINANT;
#endif
auto &p = s_txBuffer.payload;
p[0] = static_cast<uint8_t>(status->temp_raw & 0xFFU);
p[1] = static_cast<uint8_t>((status->temp_raw >> 8) & 0xFFU);
p[2] = static_cast<uint8_t>(status->vref_raw & 0xFFU);
p[3] = static_cast<uint8_t>((status->vref_raw >> 8) & 0xFFU);
p[4] = static_cast<uint8_t>(temp_x100 & 0xFF);
p[5] = static_cast<uint8_t>((temp_x100 >> 8) & 0xFF);
p[6] = static_cast<uint8_t>(vdda_x1000 & 0xFFU);
p[7] = static_cast<uint8_t>((vdda_x1000 >> 8) & 0xFFU);
p[8] = CONFIG_CAN_STATUS_TAG0;
p[9] = CONFIG_CAN_STATUS_TAG1;
p[10] = CONFIG_CAN_STATUS_TAG2;
p[11] = CONFIG_CAN_STATUS_TAG3;
for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) {
s_txBuffer.tx_message.tx_data[i] = p[i];
}
s_txBuffer.valid = 1U;
}
void CanFd::init()
{
initGpio();
setNormalMode();
can_deinit(CONFIG_CAN_INSTANCE);
initNominalTiming();
initFdMode();
filterAcceptAll();
can_interrupt_enable(CONFIG_CAN_INSTANCE, CAN_INT_RFNE0);
nvic_irq_enable(CAN0_RX0_IRQn, 1U, 1U);
s_rxFrame.valid = 0U;
s_txBuffer.valid = 0U;
}
void CanFd::poll()
{
/* 已由 CAN0_RX0_IRQHandler 中断驱动 */
}
void CanFd::prepareStatus(const StatusPayload *status)
{
if(status == nullptr) {
s_txBuffer.valid = 0U;
return;
}
buildStatusFrame(status);
}
void CanFd::sendPrepared()
{
if(s_txBuffer.valid == 0U) return;
const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &s_txBuffer.tx_message);
if(mailbox != CAN_NOMAILBOX) {
s_diag.tx_count++;
s_diag.last_payload_length = s_txBuffer.tx_message.tx_dlen;
s_diag.last_tx_id = s_txBuffer.tx_message.tx_sfid;
} else {
s_diag.error_count++;
}
}
bool CanFd::hasRxFrame()
{
return s_rxFrame.valid != 0U;
}
bool CanFd::consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length)
{
if(s_rxFrame.valid == 0U) return false;
if(id) *id = s_rxFrame.id;
if(length) *length = s_rxFrame.length;
if(data) { for(uint8_t i = 0; i < s_rxFrame.length; ++i) data[i] = s_rxFrame.data[i]; }
s_rxFrame.valid = 0U;
return true;
}
const CanFd::Diag *CanFd::diag()
{
return &s_diag;
}
void CanFd::setNormalMode()
{
gpio_bit_reset(kSlpGpio, kSlpPin);
}
void CanFd::setSilentMode()
{
gpio_bit_set(kSlpGpio, kSlpPin);
}
void CanFd::onRx0Interrupt()
{
if(can_interrupt_flag_get(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0) == RESET) return;
can_receive_message_struct rx_message{};
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message);
can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message);
s_diag.rx_count++;
s_diag.last_payload_length = rx_message.rx_dlen;
s_diag.last_rx_fd_flag = rx_message.fd_flag;
s_diag.last_rx_brs = rx_message.fd_brs;
s_diag.last_rx_esi = rx_message.fd_esi;
s_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid;
s_rxFrame.id = s_diag.last_rx_id;
s_rxFrame.length = rx_message.rx_dlen;
for(uint8_t i = 0; i < rx_message.rx_dlen; ++i)
s_rxFrame.data[i] = rx_message.rx_data[i];
s_rxFrame.valid = 1U;
can_interrupt_flag_clear(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0);
}
/* ----- 中断入口 C 包装 ------------------------------------------------- */
extern "C" void CAN0_RX0_IRQHandler(void) { CanFd::onRx0Interrupt(); }
@@ -0,0 +1,278 @@
/**
* @file CANFD.cpp
* @brief CANFD 通信外设初始化、收发与诊断实现。
*/
#include "main.h"
#include "device/canfd.h"
#include "sys/config.h"
namespace {
/** @brief 内部接收帧缓存结构。 */
struct CanfdRxFrame {
uint32_t id = 0U;
uint8_t data[64] = {0};
uint8_t length = 0U;
uint8_t valid = 0U;
};
/** @brief 内部发送帧缓存结构。 */
struct CanfdTxBuffer {
can_trasnmit_message_struct tx_message{};
uint8_t payload[64] = {0};
uint8_t valid = 0U;
};
canfd_diag_t g_can_diag{};
CanfdRxFrame g_rx_frame{};
CanfdTxBuffer g_tx_buffer{};
/**
* @brief 初始化 CAN 外设所需 GPIO 引脚。
*/
inline void can_gpio_init()
{
rcu_periph_clock_enable(CONFIG_CAN_RCU);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_pin_remap_config(GPIO_CAN0_PARTIAL_REMAP, ENABLE);
}
/**
* @brief 配置 CAN 接收过滤器为接受所有报文。
*/
inline void can_filter_accept_all()
{
can_filter_parameter_struct can_filter{};
can_filter.filter_number = 0U;
can_filter.filter_mode = CAN_FILTERMODE_MASK;
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
can_filter.filter_list_high = 0x0000U;
can_filter.filter_list_low = 0x0000U;
can_filter.filter_mask_high = 0x0000U;
can_filter.filter_mask_low = 0x0000U;
can_filter.filter_fifo_number = CAN_FIFO0;
can_filter.filter_enable = ENABLE;
can_filter_init(CONFIG_CAN_INSTANCE, &can_filter);
}
/**
* @brief 配置 CAN 标称位定时参数。
* @note 参考 APP 项目配置:仲裁段波特率为 1 Mbps,采样点约 80%。
*/
inline void can_nominal_init()
{
can_parameter_struct can_parameter{};
can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
can_parameter.time_triggered = DISABLE;
can_parameter.auto_bus_off_recovery = ENABLE;
can_parameter.auto_wake_up = DISABLE;
can_parameter.auto_retrans = ENABLE;
can_parameter.rec_fifo_overwrite = DISABLE;
can_parameter.trans_fifo_order = DISABLE;
can_parameter.working_mode = CAN_NORMAL_MODE;
can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
can_parameter.time_segment_1 = CAN_BT_BS1_7TQ;
can_parameter.time_segment_2 = CAN_BT_BS2_2TQ;
can_parameter.prescaler = 6U;
can_init(CONFIG_CAN_INSTANCE, &can_parameter);
}
/**
* @brief 配置 CANFD 可变速率模式参数。
* @note 参考 APP 项目配置:
* - 仲裁段保持 1 Mbps;
* - 数据段配置为 2 Mbps,采样点约 80%;
* - 发送时启用 BRS 后才会切到数据段速率。
*/
inline void can_fd_mode_init()
{
#ifdef GD32E508
can_fdframe_struct can_fd_parameter{};
can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter);
can_fd_parameter.fd_frame = ENABLE;
can_fd_parameter.excp_event_detect = ENABLE;
can_fd_parameter.delay_compensation = ENABLE;
can_fd_parameter.iso_bosch = CAN_FDMOD_ISO;
can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE;
can_fd_parameter.data_prescaler = 6U;
can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ;
can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ;
can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ;
can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter);
#else
can_fd_function_enable(CONFIG_CAN_INSTANCE);
#endif
}
/**
* @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。
* @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。
*/
inline void canfd_build_status_frame(const canfd_status_payload_t *status)
{
const int16_t temp_x100 = static_cast<int16_t>(status->temperature * 100.0f);
const uint16_t vdda_x1000 = static_cast<uint16_t>(status->vdda * 1000.0f);
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message);
g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID;
g_tx_buffer.tx_message.tx_efid = 0U;
g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD;
g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA;
g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN;
#ifdef GD32E508
g_tx_buffer.tx_message.fd_flag = ENABLE;
g_tx_buffer.tx_message.fd_brs = ENABLE;
g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT;
#endif
auto &p = g_tx_buffer.payload;
p[0] = static_cast<uint8_t>(status->temp_raw & 0xFFU);
p[1] = static_cast<uint8_t>((status->temp_raw >> 8) & 0xFFU);
p[2] = static_cast<uint8_t>(status->vref_raw & 0xFFU);
p[3] = static_cast<uint8_t>((status->vref_raw >> 8) & 0xFFU);
p[4] = static_cast<uint8_t>(temp_x100 & 0xFF);
p[5] = static_cast<uint8_t>((temp_x100 >> 8) & 0xFF);
p[6] = static_cast<uint8_t>(vdda_x1000 & 0xFFU);
p[7] = static_cast<uint8_t>((vdda_x1000 >> 8) & 0xFFU);
p[8] = CONFIG_CAN_STATUS_TAG0;
p[9] = CONFIG_CAN_STATUS_TAG1;
p[10] = CONFIG_CAN_STATUS_TAG2;
p[11] = CONFIG_CAN_STATUS_TAG3;
for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) {
g_tx_buffer.tx_message.tx_data[i] = p[i];
}
g_tx_buffer.valid = 1U;
}
} // namespace
/**
* @brief 初始化 CANFD 外设、GPIO、波特率参数与接收过滤器。
*/
void canfd_init(void)
{
can_gpio_init();
can_deinit(CONFIG_CAN_INSTANCE);
can_nominal_init();
can_fd_mode_init();
can_filter_accept_all();
g_rx_frame.valid = 0U;
g_tx_buffer.valid = 0U;
}
/**
* @brief 轮询 CANFD 接收 FIFO,并缓存最近收到的一帧数据。
* @note 若当前没有待接收报文,直接返回。
*/
void canfd_poll(void)
{
can_receive_message_struct rx_message{};
if(can_receive_message_length_get(CONFIG_CAN_INSTANCE, CAN_FIFO0) == 0U) {
return;
}
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message);
can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message);
g_can_diag.rx_count++;
g_can_diag.last_payload_length = rx_message.rx_dlen;
g_can_diag.last_rx_fd_flag = rx_message.fd_flag;
g_can_diag.last_rx_brs = rx_message.fd_brs;
g_can_diag.last_rx_esi = rx_message.fd_esi;
g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid;
g_rx_frame.id = g_can_diag.last_rx_id;
g_rx_frame.length = rx_message.rx_dlen;
for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) {
g_rx_frame.data[i] = rx_message.rx_data[i];
}
g_rx_frame.valid = 1U;
}
/**
* @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。
* @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。
*/
void canfd_prepare_status(const canfd_status_payload_t *status)
{
if(status == nullptr) {
g_tx_buffer.valid = 0U;
return;
}
canfd_build_status_frame(status);
}
/**
* @brief 发送已准备好的 CANFD 状态报文。
* @note 若当前没有有效待发送报文,直接返回。
*/
void canfd_send_prepared(void)
{
if(g_tx_buffer.valid == 0U) {
return;
}
const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message);
if(mailbox != CAN_NOMAILBOX) {
g_can_diag.tx_count++;
g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen;
g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid;
} else {
g_can_diag.error_count++;
}
}
/**
* @brief 查询是否存在尚未消费的接收报文。
* @retval 非 0 存在待消费报文,0 无报文。
*/
uint8_t canfd_has_rx_frame(void)
{
return g_rx_frame.valid;
}
/**
* @brief 读取并消费最近一次缓存的接收报文。
* @param[out] id 输出报文标识符的指针;允许为空。
* @param[out] data 输出报文数据缓存区指针;允许为空。
* @param[out] length 输出报文数据长度的指针;允许为空。
* @retval 1 成功消费一帧报文,0 当前无有效缓存报文。
*/
uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length)
{
if(g_rx_frame.valid == 0U) {
return 0U;
}
if(id != nullptr) {
*id = g_rx_frame.id;
}
if(length != nullptr) {
*length = g_rx_frame.length;
}
if(data != nullptr) {
for(uint8_t i = 0; i < g_rx_frame.length; ++i) {
data[i] = g_rx_frame.data[i];
}
}
g_rx_frame.valid = 0U;
return 1U;
}
/**
* @brief 获取 CANFD 诊断统计信息只读指针。
* @retval 指向内部诊断统计结构体的只读指针。
*/
const canfd_diag_t *canfd_get_diag(void)
{
return &g_can_diag;
}
@@ -0,0 +1,283 @@
/**
* @file CANFD.cpp
* @brief CANFD 通信外设初始化、收发与诊断实现。
*/
#include "main.h"
#include "device/canfd.h"
#include "sys/config.h"
namespace {
/** @brief 内部接收帧缓存结构。 */
struct CanfdRxFrame {
uint32_t id = 0U;
uint8_t data[64] = {0};
uint8_t length = 0U;
uint8_t valid = 0U;
};
/** @brief 内部发送帧缓存结构。 */
struct CanfdTxBuffer {
can_trasnmit_message_struct tx_message{};
uint8_t payload[64] = {0};
uint8_t valid = 0U;
};
canfd_diag_t g_can_diag{};
CanfdRxFrame g_rx_frame{};
CanfdTxBuffer g_tx_buffer{};
/**
* @brief 初始化 CAN 外设所需 GPIO 引脚。
*/
inline void can_gpio_init()
{
rcu_periph_clock_enable(CONFIG_CAN_RCU);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_pin_remap_config(GPIO_CAN0_PARTIAL_REMAP, ENABLE);
}
/**
* @brief 配置 CAN 接收过滤器为接受所有报文。
*/
inline void can_filter_accept_all()
{
can_filter_parameter_struct can_filter{};
can_filter.filter_number = 0U;
can_filter.filter_mode = CAN_FILTERMODE_MASK;
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
can_filter.filter_list_high = 0x0000U;
can_filter.filter_list_low = 0x0000U;
can_filter.filter_mask_high = 0x0000U;
can_filter.filter_mask_low = 0x0000U;
can_filter.filter_fifo_number = CAN_FIFO0;
can_filter.filter_enable = ENABLE;
can_filter_init(CONFIG_CAN_INSTANCE, &can_filter);
}
/**
* @brief 配置 CAN 标称位定时参数。
* @note 参考 APP 项目配置:仲裁段波特率为 1 Mbps,采样点约 80%。
*/
inline void can_nominal_init()
{
can_parameter_struct can_parameter{};
can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
can_parameter.time_triggered = DISABLE;
can_parameter.auto_bus_off_recovery = ENABLE;
can_parameter.auto_wake_up = DISABLE;
can_parameter.auto_retrans = ENABLE;
can_parameter.rec_fifo_overwrite = ENABLE;
can_parameter.trans_fifo_order = ENABLE;
can_parameter.working_mode = CAN_NORMAL_MODE;
can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
can_parameter.time_segment_1 = CAN_BT_BS1_7TQ;
can_parameter.time_segment_2 = CAN_BT_BS2_2TQ;
can_parameter.prescaler = 6U;
can_init(CONFIG_CAN_INSTANCE, &can_parameter);
}
/**
* @brief 配置 CANFD 可变速率模式参数。
* @note 参考 APP 项目配置:
* - 仲裁段保持 1 Mbps;
* - 数据段配置为 2 Mbps,采样点约 80%;
* - 发送时启用 BRS 后才会切到数据段速率。
*/
inline void can_fd_mode_init()
{
#ifdef GD32E508
can_fdframe_struct can_fd_parameter{};
can_fd_tdc_struct can_fd_tdc_parameter{};
can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter);
can_fd_parameter.fd_frame = ENABLE;
can_fd_parameter.excp_event_detect = ENABLE;
can_fd_parameter.delay_compensation = ENABLE;
can_fd_tdc_parameter.tdc_filter = 0x04U;
can_fd_tdc_parameter.tdc_mode = CAN_TDCMOD_CALC_AND_OFFSET;
can_fd_tdc_parameter.tdc_offset = 0x04U;
can_fd_parameter.p_delay_compensation = &can_fd_tdc_parameter;
can_fd_parameter.iso_bosch = CAN_FDMOD_ISO;
can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE;
can_fd_parameter.data_prescaler = 6U;
can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ;
can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ;
can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ;
can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter);
#else
can_fd_function_enable(CONFIG_CAN_INSTANCE);
#endif
}
/**
* @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。
* @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。
*/
inline void canfd_build_status_frame(const canfd_status_payload_t *status)
{
const int16_t temp_x100 = static_cast<int16_t>(status->temperature * 100.0f);
const uint16_t vdda_x1000 = static_cast<uint16_t>(status->vdda * 1000.0f);
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message);
g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID;
g_tx_buffer.tx_message.tx_efid = 0U;
g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD;
g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA;
g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN;
#ifdef GD32E508
g_tx_buffer.tx_message.fd_flag = ENABLE;
g_tx_buffer.tx_message.fd_brs = ENABLE;
g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT;
#endif
auto &p = g_tx_buffer.payload;
p[0] = static_cast<uint8_t>(status->temp_raw & 0xFFU);
p[1] = static_cast<uint8_t>((status->temp_raw >> 8) & 0xFFU);
p[2] = static_cast<uint8_t>(status->vref_raw & 0xFFU);
p[3] = static_cast<uint8_t>((status->vref_raw >> 8) & 0xFFU);
p[4] = static_cast<uint8_t>(temp_x100 & 0xFF);
p[5] = static_cast<uint8_t>((temp_x100 >> 8) & 0xFF);
p[6] = static_cast<uint8_t>(vdda_x1000 & 0xFFU);
p[7] = static_cast<uint8_t>((vdda_x1000 >> 8) & 0xFFU);
p[8] = CONFIG_CAN_STATUS_TAG0;
p[9] = CONFIG_CAN_STATUS_TAG1;
p[10] = CONFIG_CAN_STATUS_TAG2;
p[11] = CONFIG_CAN_STATUS_TAG3;
for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) {
g_tx_buffer.tx_message.tx_data[i] = p[i];
}
g_tx_buffer.valid = 1U;
}
} // namespace
/**
* @brief 初始化 CANFD 外设、GPIO、波特率参数与接收过滤器。
*/
void canfd_init(void)
{
can_gpio_init();
can_deinit(CONFIG_CAN_INSTANCE);
can_nominal_init();
can_fd_mode_init();
can_filter_accept_all();
g_rx_frame.valid = 0U;
g_tx_buffer.valid = 0U;
}
/**
* @brief 轮询 CANFD 接收 FIFO,并缓存最近收到的一帧数据。
* @note 若当前没有待接收报文,直接返回。
*/
void canfd_poll(void)
{
can_receive_message_struct rx_message{};
if(can_receive_message_length_get(CONFIG_CAN_INSTANCE, CAN_FIFO0) == 0U) {
return;
}
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message);
can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message);
g_can_diag.rx_count++;
g_can_diag.last_payload_length = rx_message.rx_dlen;
g_can_diag.last_rx_fd_flag = rx_message.fd_flag;
g_can_diag.last_rx_brs = rx_message.fd_brs;
g_can_diag.last_rx_esi = rx_message.fd_esi;
g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid;
g_rx_frame.id = g_can_diag.last_rx_id;
g_rx_frame.length = rx_message.rx_dlen;
for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) {
g_rx_frame.data[i] = rx_message.rx_data[i];
}
g_rx_frame.valid = 1U;
}
/**
* @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。
* @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。
*/
void canfd_prepare_status(const canfd_status_payload_t *status)
{
if(status == nullptr) {
g_tx_buffer.valid = 0U;
return;
}
canfd_build_status_frame(status);
}
/**
* @brief 发送已准备好的 CANFD 状态报文。
* @note 若当前没有有效待发送报文,直接返回。
*/
void canfd_send_prepared(void)
{
if(g_tx_buffer.valid == 0U) {
return;
}
const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message);
if(mailbox != CAN_NOMAILBOX) {
g_can_diag.tx_count++;
g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen;
g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid;
} else {
g_can_diag.error_count++;
}
}
/**
* @brief 查询是否存在尚未消费的接收报文。
* @retval 非 0 存在待消费报文,0 无报文。
*/
uint8_t canfd_has_rx_frame(void)
{
return g_rx_frame.valid;
}
/**
* @brief 读取并消费最近一次缓存的接收报文。
* @param[out] id 输出报文标识符的指针;允许为空。
* @param[out] data 输出报文数据缓存区指针;允许为空。
* @param[out] length 输出报文数据长度的指针;允许为空。
* @retval 1 成功消费一帧报文,0 当前无有效缓存报文。
*/
uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length)
{
if(g_rx_frame.valid == 0U) {
return 0U;
}
if(id != nullptr) {
*id = g_rx_frame.id;
}
if(length != nullptr) {
*length = g_rx_frame.length;
}
if(data != nullptr) {
for(uint8_t i = 0; i < g_rx_frame.length; ++i) {
data[i] = g_rx_frame.data[i];
}
}
g_rx_frame.valid = 0U;
return 1U;
}
/**
* @brief 获取 CANFD 诊断统计信息只读指针。
* @retval 指向内部诊断统计结构体的只读指针。
*/
const canfd_diag_t *canfd_get_diag(void)
{
return &g_can_diag;
}
@@ -0,0 +1,274 @@
/**
* @file CANFD.cpp
* @brief CANFD 通信外设初始化、收发与诊断实现。
*/
#include "main.h"
#include "device/canfd.h"
#include "sys/config.h"
namespace {
/** @brief 内部接收帧缓存结构。 */
struct CanfdRxFrame {
uint32_t id = 0U;
uint8_t data[64] = {0};
uint8_t length = 0U;
uint8_t valid = 0U;
};
/** @brief 内部发送帧缓存结构。 */
struct CanfdTxBuffer {
can_trasnmit_message_struct tx_message{};
uint8_t payload[64] = {0};
uint8_t valid = 0U;
};
canfd_diag_t g_can_diag{};
CanfdRxFrame g_rx_frame{};
CanfdTxBuffer g_tx_buffer{};
/**
* @brief 初始化 CAN 外设所需 GPIO 引脚。
*/
inline void can_gpio_init()
{
rcu_periph_clock_enable(CONFIG_CAN_RCU);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_pin_remap_config(GPIO_CAN0_PARTIAL_REMAP, ENABLE);
}
/**
* @brief 配置 CAN 接收过滤器为接受所有报文。
*/
inline void can_filter_accept_all()
{
can_filter_parameter_struct can_filter{};
can_filter.filter_number = 0U;
can_filter.filter_mode = CAN_FILTERMODE_MASK;
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
can_filter.filter_list_high = 0x0000U;
can_filter.filter_list_low = 0x0000U;
can_filter.filter_mask_high = 0x0000U;
can_filter.filter_mask_low = 0x0000U;
can_filter.filter_fifo_number = CAN_FIFO0;
can_filter.filter_enable = ENABLE;
can_filter_init(CONFIG_CAN_INSTANCE, &can_filter);
}
/**
* @brief 配置 CAN 标称位定时参数。
*/
inline void can_nominal_init()
{
can_parameter_struct can_parameter{};
can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
can_parameter.time_triggered = DISABLE;
can_parameter.auto_bus_off_recovery = ENABLE;
can_parameter.auto_wake_up = DISABLE;
can_parameter.auto_retrans = ENABLE;
can_parameter.rec_fifo_overwrite = DISABLE;
can_parameter.trans_fifo_order = DISABLE;
can_parameter.working_mode = CAN_NORMAL_MODE;
can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
can_parameter.time_segment_1 = CAN_BT_BS1_8TQ;
can_parameter.time_segment_2 = CAN_BT_BS2_3TQ;
can_parameter.prescaler = CONFIG_CAN_NOMINAL_BAUD_PRESCALER;
can_init(CONFIG_CAN_INSTANCE, &can_parameter);
}
/**
* @brief 配置 CANFD 可变速率模式参数。
* @note 在 GD32E508 上启用 ISO 模式;非 GD32E508 平台仅启用 FD 功能。
*/
inline void can_fd_mode_init()
{
#ifdef GD32E508
can_fdframe_struct can_fd_parameter{};
can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter);
can_fd_parameter.fd_frame = ENABLE;
can_fd_parameter.excp_event_detect = ENABLE;
can_fd_parameter.delay_compensation = DISABLE;
can_fd_parameter.iso_bosch = CAN_FDMOD_ISO;
can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE;
can_fd_parameter.data_prescaler = 1U;
can_fd_parameter.data_resync_jump_width = 0U;
can_fd_parameter.data_time_segment_1 = 2U;
can_fd_parameter.data_time_segment_2 = 1U;
can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter);
#else
can_fd_function_enable(CONFIG_CAN_INSTANCE);
#endif
}
/**
* @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。
* @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。
*/
inline void canfd_build_status_frame(const canfd_status_payload_t *status)
{
const int16_t temp_x100 = static_cast<int16_t>(status->temperature * 100.0f);
const uint16_t vdda_x1000 = static_cast<uint16_t>(status->vdda * 1000.0f);
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message);
g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID;
g_tx_buffer.tx_message.tx_efid = 0U;
g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD;
g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA;
g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN;
#ifdef GD32E508
g_tx_buffer.tx_message.fd_flag = ENABLE;
g_tx_buffer.tx_message.fd_brs = CAN_BRS_DISABLE;
g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT;
#endif
auto &p = g_tx_buffer.payload;
p[0] = static_cast<uint8_t>(status->temp_raw & 0xFFU);
p[1] = static_cast<uint8_t>((status->temp_raw >> 8) & 0xFFU);
p[2] = static_cast<uint8_t>(status->vref_raw & 0xFFU);
p[3] = static_cast<uint8_t>((status->vref_raw >> 8) & 0xFFU);
p[4] = static_cast<uint8_t>(temp_x100 & 0xFF);
p[5] = static_cast<uint8_t>((temp_x100 >> 8) & 0xFF);
p[6] = static_cast<uint8_t>(vdda_x1000 & 0xFFU);
p[7] = static_cast<uint8_t>((vdda_x1000 >> 8) & 0xFFU);
p[8] = CONFIG_CAN_STATUS_TAG0;
p[9] = CONFIG_CAN_STATUS_TAG1;
p[10] = CONFIG_CAN_STATUS_TAG2;
p[11] = CONFIG_CAN_STATUS_TAG3;
for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) {
g_tx_buffer.tx_message.tx_data[i] = p[i];
}
g_tx_buffer.valid = 1U;
}
} // namespace
/**
* @brief 初始化 CANFD 外设、GPIO、波特率参数与接收过滤器。
*/
void canfd_init(void)
{
can_gpio_init();
can_deinit(CONFIG_CAN_INSTANCE);
can_nominal_init();
can_fd_mode_init();
can_filter_accept_all();
g_rx_frame.valid = 0U;
g_tx_buffer.valid = 0U;
}
/**
* @brief 轮询 CANFD 接收 FIFO,并缓存最近收到的一帧数据。
* @note 若当前没有待接收报文,直接返回。
*/
void canfd_poll(void)
{
can_receive_message_struct rx_message{};
if(can_receive_message_length_get(CONFIG_CAN_INSTANCE, CAN_FIFO0) == 0U) {
return;
}
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message);
can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message);
g_can_diag.rx_count++;
g_can_diag.last_payload_length = rx_message.rx_dlen;
g_can_diag.last_rx_fd_flag = rx_message.fd_flag;
g_can_diag.last_rx_brs = rx_message.fd_brs;
g_can_diag.last_rx_esi = rx_message.fd_esi;
g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid;
g_rx_frame.id = g_can_diag.last_rx_id;
g_rx_frame.length = rx_message.rx_dlen;
for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) {
g_rx_frame.data[i] = rx_message.rx_data[i];
}
g_rx_frame.valid = 1U;
}
/**
* @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。
* @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。
*/
void canfd_prepare_status(const canfd_status_payload_t *status)
{
if(status == nullptr) {
g_tx_buffer.valid = 0U;
return;
}
canfd_build_status_frame(status);
}
/**
* @brief 发送已准备好的 CANFD 状态报文。
* @note 若当前没有有效待发送报文,直接返回。
*/
void canfd_send_prepared(void)
{
if(g_tx_buffer.valid == 0U) {
return;
}
const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message);
if(mailbox != CAN_NOMAILBOX) {
g_can_diag.tx_count++;
g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen;
g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid;
} else {
g_can_diag.error_count++;
}
}
/**
* @brief 查询是否存在尚未消费的接收报文。
* @retval 非 0 存在待消费报文,0 无报文。
*/
uint8_t canfd_has_rx_frame(void)
{
return g_rx_frame.valid;
}
/**
* @brief 读取并消费最近一次缓存的接收报文。
* @param[out] id 输出报文标识符的指针;允许为空。
* @param[out] data 输出报文数据缓存区指针;允许为空。
* @param[out] length 输出报文数据长度的指针;允许为空。
* @retval 1 成功消费一帧报文,0 当前无有效缓存报文。
*/
uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length)
{
if(g_rx_frame.valid == 0U) {
return 0U;
}
if(id != nullptr) {
*id = g_rx_frame.id;
}
if(length != nullptr) {
*length = g_rx_frame.length;
}
if(data != nullptr) {
for(uint8_t i = 0; i < g_rx_frame.length; ++i) {
data[i] = g_rx_frame.data[i];
}
}
g_rx_frame.valid = 0U;
return 1U;
}
/**
* @brief 获取 CANFD 诊断统计信息只读指针。
* @retval 指向内部诊断统计结构体的只读指针。
*/
const canfd_diag_t *canfd_get_diag(void)
{
return &g_can_diag;
}
@@ -0,0 +1,274 @@
/**
* @file CANFD.cpp
* @brief CANFD 通信外设初始化、收发与诊断实现。
*/
#include "main.h"
#include "device/canfd.h"
#include "sys/config.h"
namespace {
/** @brief 内部接收帧缓存结构。 */
struct CanfdRxFrame {
uint32_t id = 0U;
uint8_t data[64] = {0};
uint8_t length = 0U;
uint8_t valid = 0U;
};
/** @brief 内部发送帧缓存结构。 */
struct CanfdTxBuffer {
can_trasnmit_message_struct tx_message{};
uint8_t payload[64] = {0};
uint8_t valid = 0U;
};
canfd_diag_t g_can_diag{};
CanfdRxFrame g_rx_frame{};
CanfdTxBuffer g_tx_buffer{};
/**
* @brief 初始化 CAN 外设所需 GPIO 引脚。
*/
inline void can_gpio_init()
{
rcu_periph_clock_enable(CONFIG_CAN_INSTANCE);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_pin_remap_config(GPIO_CAN0_PARTIAL_REMAP, ENABLE);
}
/**
* @brief 配置 CAN 接收过滤器为接受所有报文。
*/
inline void can_filter_accept_all()
{
can_filter_parameter_struct can_filter{};
can_filter.filter_number = 0U;
can_filter.filter_mode = CAN_FILTERMODE_MASK;
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
can_filter.filter_list_high = 0x0000U;
can_filter.filter_list_low = 0x0000U;
can_filter.filter_mask_high = 0x0000U;
can_filter.filter_mask_low = 0x0000U;
can_filter.filter_fifo_number = CAN_FIFO0;
can_filter.filter_enable = ENABLE;
can_filter_init(CONFIG_CAN_INSTANCE, &can_filter);
}
/**
* @brief 配置 CAN 标称位定时参数。
*/
inline void can_nominal_init()
{
can_parameter_struct can_parameter{};
can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
can_parameter.time_triggered = DISABLE;
can_parameter.auto_bus_off_recovery = ENABLE;
can_parameter.auto_wake_up = DISABLE;
can_parameter.auto_retrans = ENABLE;
can_parameter.rec_fifo_overwrite = DISABLE;
can_parameter.trans_fifo_order = DISABLE;
can_parameter.working_mode = CAN_NORMAL_MODE;
can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
can_parameter.time_segment_1 = CAN_BT_BS1_8TQ;
can_parameter.time_segment_2 = CAN_BT_BS2_3TQ;
can_parameter.prescaler = CONFIG_CAN_NOMINAL_BAUD_PRESCALER;
can_init(CONFIG_CAN_INSTANCE, &can_parameter);
}
/**
* @brief 配置 CANFD 可变速率模式参数。
* @note 在 GD32E508 上启用 ISO 模式;非 GD32E508 平台仅启用 FD 功能。
*/
inline void can_fd_mode_init()
{
#ifdef GD32E508
can_fdframe_struct can_fd_parameter{};
can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter);
can_fd_parameter.fd_frame = ENABLE;
can_fd_parameter.excp_event_detect = ENABLE;
can_fd_parameter.delay_compensation = DISABLE;
can_fd_parameter.iso_bosch = CAN_FDMOD_ISO;
can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE;
can_fd_parameter.data_prescaler = 1U;
can_fd_parameter.data_resync_jump_width = 0U;
can_fd_parameter.data_time_segment_1 = 2U;
can_fd_parameter.data_time_segment_2 = 1U;
can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter);
#else
can_fd_function_enable(CONFIG_CAN_INSTANCE);
#endif
}
/**
* @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。
* @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。
*/
inline void canfd_build_status_frame(const canfd_status_payload_t *status)
{
const int16_t temp_x100 = static_cast<int16_t>(status->temperature * 100.0f);
const uint16_t vdda_x1000 = static_cast<uint16_t>(status->vdda * 1000.0f);
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message);
g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID;
g_tx_buffer.tx_message.tx_efid = 0U;
g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD;
g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA;
g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN;
#ifdef GD32E508
g_tx_buffer.tx_message.fd_flag = ENABLE;
g_tx_buffer.tx_message.fd_brs = CAN_BRS_DISABLE;
g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT;
#endif
auto &p = g_tx_buffer.payload;
p[0] = static_cast<uint8_t>(status->temp_raw & 0xFFU);
p[1] = static_cast<uint8_t>((status->temp_raw >> 8) & 0xFFU);
p[2] = static_cast<uint8_t>(status->vref_raw & 0xFFU);
p[3] = static_cast<uint8_t>((status->vref_raw >> 8) & 0xFFU);
p[4] = static_cast<uint8_t>(temp_x100 & 0xFF);
p[5] = static_cast<uint8_t>((temp_x100 >> 8) & 0xFF);
p[6] = static_cast<uint8_t>(vdda_x1000 & 0xFFU);
p[7] = static_cast<uint8_t>((vdda_x1000 >> 8) & 0xFFU);
p[8] = CONFIG_CAN_STATUS_TAG0;
p[9] = CONFIG_CAN_STATUS_TAG1;
p[10] = CONFIG_CAN_STATUS_TAG2;
p[11] = CONFIG_CAN_STATUS_TAG3;
for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) {
g_tx_buffer.tx_message.tx_data[i] = p[i];
}
g_tx_buffer.valid = 1U;
}
} // namespace
/**
* @brief 初始化 CANFD 外设、GPIO、波特率参数与接收过滤器。
*/
void canfd_init(void)
{
can_gpio_init();
can_deinit(CONFIG_CAN_INSTANCE);
can_nominal_init();
can_fd_mode_init();
can_filter_accept_all();
g_rx_frame.valid = 0U;
g_tx_buffer.valid = 0U;
}
/**
* @brief 轮询 CANFD 接收 FIFO,并缓存最近收到的一帧数据。
* @note 若当前没有待接收报文,直接返回。
*/
void canfd_poll(void)
{
can_receive_message_struct rx_message{};
if(can_receive_message_length_get(CONFIG_CAN_INSTANCE, CAN_FIFO0) == 0U) {
return;
}
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message);
can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message);
g_can_diag.rx_count++;
g_can_diag.last_payload_length = rx_message.rx_dlen;
g_can_diag.last_rx_fd_flag = rx_message.fd_flag;
g_can_diag.last_rx_brs = rx_message.fd_brs;
g_can_diag.last_rx_esi = rx_message.fd_esi;
g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid;
g_rx_frame.id = g_can_diag.last_rx_id;
g_rx_frame.length = rx_message.rx_dlen;
for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) {
g_rx_frame.data[i] = rx_message.rx_data[i];
}
g_rx_frame.valid = 1U;
}
/**
* @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。
* @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。
*/
void canfd_prepare_status(const canfd_status_payload_t *status)
{
if(status == nullptr) {
g_tx_buffer.valid = 0U;
return;
}
canfd_build_status_frame(status);
}
/**
* @brief 发送已准备好的 CANFD 状态报文。
* @note 若当前没有有效待发送报文,直接返回。
*/
void canfd_send_prepared(void)
{
if(g_tx_buffer.valid == 0U) {
return;
}
const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message);
if(mailbox != CAN_NOMAILBOX) {
g_can_diag.tx_count++;
g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen;
g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid;
} else {
g_can_diag.error_count++;
}
}
/**
* @brief 查询是否存在尚未消费的接收报文。
* @retval 非 0 存在待消费报文,0 无报文。
*/
uint8_t canfd_has_rx_frame(void)
{
return g_rx_frame.valid;
}
/**
* @brief 读取并消费最近一次缓存的接收报文。
* @param[out] id 输出报文标识符的指针;允许为空。
* @param[out] data 输出报文数据缓存区指针;允许为空。
* @param[out] length 输出报文数据长度的指针;允许为空。
* @retval 1 成功消费一帧报文,0 当前无有效缓存报文。
*/
uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length)
{
if(g_rx_frame.valid == 0U) {
return 0U;
}
if(id != nullptr) {
*id = g_rx_frame.id;
}
if(length != nullptr) {
*length = g_rx_frame.length;
}
if(data != nullptr) {
for(uint8_t i = 0; i < g_rx_frame.length; ++i) {
data[i] = g_rx_frame.data[i];
}
}
g_rx_frame.valid = 0U;
return 1U;
}
/**
* @brief 获取 CANFD 诊断统计信息只读指针。
* @retval 指向内部诊断统计结构体的只读指针。
*/
const canfd_diag_t *canfd_get_diag(void)
{
return &g_can_diag;
}
@@ -0,0 +1,225 @@
/**
* @file CANFD.cpp
* @brief CANFD 通信模块实现。
*/
#include "main.h"
#include "device/canfd.h"
#include "sys/config.h"
namespace {
constexpr uint32_t kSlpGpio = GPIOA;
constexpr uint32_t kSlpPin = GPIO_PIN_8;
} // namespace
CanFd::Diag CanFd::s_diag;
CanFd::RxFrame CanFd::s_rxFrame;
CanFd::TxBuffer CanFd::s_txBuffer;
void CanFd::initGpio()
{
rcu_periph_clock_enable(CONFIG_CAN_RCU);
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_11);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);
gpio_init(kSlpGpio, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, kSlpPin);
}
void CanFd::filterAcceptAll()
{
can_filter_parameter_struct can_filter{};
can_filter.filter_number = 0U;
can_filter.filter_mode = CAN_FILTERMODE_MASK;
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
can_filter.filter_list_high = 0x0000U;
can_filter.filter_list_low = 0x0000U;
can_filter.filter_mask_high = 0x0000U;
can_filter.filter_mask_low = 0x0000U;
can_filter.filter_fifo_number = CAN_FIFO0;
can_filter.filter_enable = ENABLE;
can_filter_init(CONFIG_CAN_INSTANCE, &can_filter);
}
void CanFd::initNominalTiming()
{
can_parameter_struct can_parameter{};
can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
can_parameter.time_triggered = DISABLE;
can_parameter.auto_bus_off_recovery = ENABLE;
can_parameter.auto_wake_up = DISABLE;
can_parameter.auto_retrans = ENABLE;
can_parameter.rec_fifo_overwrite = ENABLE;
can_parameter.trans_fifo_order = ENABLE;
can_parameter.working_mode = CAN_NORMAL_MODE;
can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
can_parameter.time_segment_1 = CAN_BT_BS1_7TQ;
can_parameter.time_segment_2 = CAN_BT_BS2_2TQ;
can_parameter.prescaler = 6U;
can_init(CONFIG_CAN_INSTANCE, &can_parameter);
}
void CanFd::initFdMode()
{
#ifdef GD32E508
can_fdframe_struct can_fd_parameter{};
can_fd_tdc_struct can_fd_tdc_parameter{};
can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter);
can_fd_parameter.fd_frame = ENABLE;
can_fd_parameter.excp_event_detect = ENABLE;
can_fd_parameter.delay_compensation = ENABLE;
can_fd_tdc_parameter.tdc_filter = 0x04U;
can_fd_tdc_parameter.tdc_mode = CAN_TDCMOD_CALC_AND_OFFSET;
can_fd_tdc_parameter.tdc_offset = 0x04U;
can_fd_parameter.p_delay_compensation = &can_fd_tdc_parameter;
can_fd_parameter.iso_bosch = CAN_FDMOD_ISO;
can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE;
can_fd_parameter.data_prescaler = 6U;
can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ;
can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ;
can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ;
can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter);
#else
can_fd_function_enable(CONFIG_CAN_INSTANCE);
#endif
}
void CanFd::buildStatusFrame(const StatusPayload *status)
{
const int16_t temp_x100 = static_cast<int16_t>(status->temperature * 100.0f);
const uint16_t vdda_x1000 = static_cast<uint16_t>(status->vdda * 1000.0f);
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &s_txBuffer.tx_message);
s_txBuffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID;
s_txBuffer.tx_message.tx_efid = 0U;
s_txBuffer.tx_message.tx_ff = CAN_FF_STANDARD;
s_txBuffer.tx_message.tx_ft = CAN_FT_DATA;
s_txBuffer.tx_message.tx_dlen = CANFD_STATUS_DLEN;
#ifdef GD32E508
s_txBuffer.tx_message.fd_flag = ENABLE;
s_txBuffer.tx_message.fd_brs = ENABLE;
s_txBuffer.tx_message.fd_esi = CAN_ESI_DOMINANT;
#endif
auto &p = s_txBuffer.payload;
p[0] = static_cast<uint8_t>(status->temp_raw & 0xFFU);
p[1] = static_cast<uint8_t>((status->temp_raw >> 8) & 0xFFU);
p[2] = static_cast<uint8_t>(status->vref_raw & 0xFFU);
p[3] = static_cast<uint8_t>((status->vref_raw >> 8) & 0xFFU);
p[4] = static_cast<uint8_t>(temp_x100 & 0xFF);
p[5] = static_cast<uint8_t>((temp_x100 >> 8) & 0xFF);
p[6] = static_cast<uint8_t>(vdda_x1000 & 0xFFU);
p[7] = static_cast<uint8_t>((vdda_x1000 >> 8) & 0xFFU);
p[8] = CONFIG_CAN_STATUS_TAG0;
p[9] = CONFIG_CAN_STATUS_TAG1;
p[10] = CONFIG_CAN_STATUS_TAG2;
p[11] = CONFIG_CAN_STATUS_TAG3;
for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) {
s_txBuffer.tx_message.tx_data[i] = p[i];
}
s_txBuffer.valid = 1U;
}
void CanFd::init()
{
initGpio();
setNormalMode();
can_deinit(CONFIG_CAN_INSTANCE);
initNominalTiming();
initFdMode();
filterAcceptAll();
can_interrupt_enable(CONFIG_CAN_INSTANCE, CAN_INT_RFNE0);
nvic_irq_enable(CAN0_RX0_IRQn, 1U, 1U);
s_rxFrame.valid = 0U;
s_txBuffer.valid = 0U;
}
void CanFd::poll()
{
/* 已由 CAN0_RX0_IRQHandler 中断驱动 */
}
void CanFd::prepareStatus(const StatusPayload *status)
{
if(status == nullptr) {
s_txBuffer.valid = 0U;
return;
}
buildStatusFrame(status);
}
void CanFd::sendPrepared()
{
if(s_txBuffer.valid == 0U) return;
const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &s_txBuffer.tx_message);
if(mailbox != CAN_NOMAILBOX) {
s_diag.tx_count++;
s_diag.last_payload_length = s_txBuffer.tx_message.tx_dlen;
s_diag.last_tx_id = s_txBuffer.tx_message.tx_sfid;
} else {
s_diag.error_count++;
}
}
bool CanFd::hasRxFrame()
{
return s_rxFrame.valid != 0U;
}
bool CanFd::consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length)
{
if(s_rxFrame.valid == 0U) return false;
if(id) *id = s_rxFrame.id;
if(length) *length = s_rxFrame.length;
if(data) { for(uint8_t i = 0; i < s_rxFrame.length; ++i) data[i] = s_rxFrame.data[i]; }
s_rxFrame.valid = 0U;
return true;
}
const CanFd::Diag *CanFd::diag()
{
return &s_diag;
}
void CanFd::setNormalMode()
{
gpio_bit_reset(kSlpGpio, kSlpPin);
}
void CanFd::setSilentMode()
{
gpio_bit_set(kSlpGpio, kSlpPin);
}
void CanFd::onRx0Interrupt()
{
if(can_interrupt_flag_get(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0) == RESET) return;
can_receive_message_struct rx_message{};
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message);
can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message);
s_diag.rx_count++;
s_diag.last_payload_length = rx_message.rx_dlen;
s_diag.last_rx_fd_flag = rx_message.fd_flag;
s_diag.last_rx_brs = rx_message.fd_brs;
s_diag.last_rx_esi = rx_message.fd_esi;
s_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid;
s_rxFrame.id = s_diag.last_rx_id;
s_rxFrame.length = rx_message.rx_dlen;
for(uint8_t i = 0; i < rx_message.rx_dlen; ++i)
s_rxFrame.data[i] = rx_message.rx_data[i];
s_rxFrame.valid = 1U;
can_interrupt_flag_clear(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0);
}
/* ----- 中断入口 C 包装 ------------------------------------------------- */
extern "C" void CAN0_RX0_IRQHandler(void) { CanFd::onRx0Interrupt(); }
+326
View File
@@ -0,0 +1,326 @@
/**
* @file CANFD.cpp
* @brief CANFD 通信外设初始化、收发与诊断实现。
*/
#include "main.h"
#include "device/canfd.h"
#include "sys/config.h"
namespace {
constexpr uint32_t kCanSlpGpio = GPIOA;
constexpr uint32_t kCanSlpPin = GPIO_PIN_8;
/** @brief 内部接收帧缓存结构。 */
struct CanfdRxFrame {
uint32_t id = 0U;
uint8_t data[64] = {0};
uint8_t length = 0U;
uint8_t valid = 0U;
};
/** @brief 内部发送帧缓存结构。 */
struct CanfdTxBuffer {
can_trasnmit_message_struct tx_message{};
uint8_t payload[64] = {0};
uint8_t valid = 0U;
};
canfd_diag_t g_can_diag{};
CanfdRxFrame g_rx_frame{};
CanfdTxBuffer g_tx_buffer{};
/**
* @brief 初始化 CAN0 外设所需 GPIO 引脚。
* @note 按原理图连接关系配置:
* - PA12 -> CAN0_TX
* - PA11 -> CAN0_RX
* - PA8 -> CAN0_SLP(连接 TCAN1057A #0 模式控制脚)
*/
inline void can_gpio_init()
{
rcu_periph_clock_enable(CONFIG_CAN_RCU);
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_11);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);
gpio_init(kCanSlpGpio, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, kCanSlpPin);
}
/**
* @brief 将 TCAN1057A #0 置为正常工作模式。
* @note 模式控制脚按 STB 语义处理:低电平为 Normal,高电平为 Silent。
*/
inline void can_transceiver_normal_mode()
{
gpio_bit_reset(kCanSlpGpio, kCanSlpPin);
}
/**
* @brief 将 TCAN1057A #0 置为静默模式。
* @note 静默模式下收发器不驱动总线显性位,可用于总线监听或受控隔离场景。
*/
inline void can_transceiver_silent_mode()
{
gpio_bit_set(kCanSlpGpio, kCanSlpPin);
}
/**
* @brief 配置 CAN 接收过滤器为接受所有报文。
*/
inline void can_filter_accept_all()
{
can_filter_parameter_struct can_filter{};
can_filter.filter_number = 0U;
can_filter.filter_mode = CAN_FILTERMODE_MASK;
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
can_filter.filter_list_high = 0x0000U;
can_filter.filter_list_low = 0x0000U;
can_filter.filter_mask_high = 0x0000U;
can_filter.filter_mask_low = 0x0000U;
can_filter.filter_fifo_number = CAN_FIFO0;
can_filter.filter_enable = ENABLE;
can_filter_init(CONFIG_CAN_INSTANCE, &can_filter);
}
/**
* @brief 配置 CAN 标称位定时参数。
* @note 参考 APP 项目配置:仲裁段波特率为 1 Mbps,采样点约 80%。
*/
inline void can_nominal_init()
{
can_parameter_struct can_parameter{};
can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
can_parameter.time_triggered = DISABLE;
can_parameter.auto_bus_off_recovery = ENABLE;
can_parameter.auto_wake_up = DISABLE;
can_parameter.auto_retrans = ENABLE;
can_parameter.rec_fifo_overwrite = ENABLE;
can_parameter.trans_fifo_order = ENABLE;
can_parameter.working_mode = CAN_NORMAL_MODE;
can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
can_parameter.time_segment_1 = CAN_BT_BS1_7TQ;
can_parameter.time_segment_2 = CAN_BT_BS2_2TQ;
can_parameter.prescaler = 6U;
can_init(CONFIG_CAN_INSTANCE, &can_parameter);
}
/**
* @brief 配置 CANFD 可变速率模式参数。
* @note 参考 APP 项目配置:
* - 仲裁段保持 1 Mbps;
* - 数据段配置为 2 Mbps,采样点约 80%;
* - 发送时启用 BRS 后才会切到数据段速率。
*/
inline void can_fd_mode_init()
{
#ifdef GD32E508
can_fdframe_struct can_fd_parameter{};
can_fd_tdc_struct can_fd_tdc_parameter{};
can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter);
can_fd_parameter.fd_frame = ENABLE;
can_fd_parameter.excp_event_detect = ENABLE;
can_fd_parameter.delay_compensation = ENABLE;
can_fd_tdc_parameter.tdc_filter = 0x04U;
can_fd_tdc_parameter.tdc_mode = CAN_TDCMOD_CALC_AND_OFFSET;
can_fd_tdc_parameter.tdc_offset = 0x04U;
can_fd_parameter.p_delay_compensation = &can_fd_tdc_parameter;
can_fd_parameter.iso_bosch = CAN_FDMOD_ISO;
can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE;
can_fd_parameter.data_prescaler = 6U;
can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ;
can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ;
can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ;
can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter);
#else
can_fd_function_enable(CONFIG_CAN_INSTANCE);
#endif
}
/**
* @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。
* @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。
*/
inline void canfd_build_status_frame(const canfd_status_payload_t *status)
{
const int16_t temp_x100 = static_cast<int16_t>(status->temperature * 100.0f);
const uint16_t vdda_x1000 = static_cast<uint16_t>(status->vdda * 1000.0f);
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message);
g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID;
g_tx_buffer.tx_message.tx_efid = 0U;
g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD;
g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA;
g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN;
#ifdef GD32E508
g_tx_buffer.tx_message.fd_flag = ENABLE;
g_tx_buffer.tx_message.fd_brs = ENABLE;
g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT;
#endif
auto &p = g_tx_buffer.payload;
p[0] = static_cast<uint8_t>(status->temp_raw & 0xFFU);
p[1] = static_cast<uint8_t>((status->temp_raw >> 8) & 0xFFU);
p[2] = static_cast<uint8_t>(status->vref_raw & 0xFFU);
p[3] = static_cast<uint8_t>((status->vref_raw >> 8) & 0xFFU);
p[4] = static_cast<uint8_t>(temp_x100 & 0xFF);
p[5] = static_cast<uint8_t>((temp_x100 >> 8) & 0xFF);
p[6] = static_cast<uint8_t>(vdda_x1000 & 0xFFU);
p[7] = static_cast<uint8_t>((vdda_x1000 >> 8) & 0xFFU);
p[8] = CONFIG_CAN_STATUS_TAG0;
p[9] = CONFIG_CAN_STATUS_TAG1;
p[10] = CONFIG_CAN_STATUS_TAG2;
p[11] = CONFIG_CAN_STATUS_TAG3;
for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) {
g_tx_buffer.tx_message.tx_data[i] = p[i];
}
g_tx_buffer.valid = 1U;
}
} // namespace
/**
* @brief CAN0 RX0 中断服务例程。
* @note 在 CANFD 帧到达 FIFO0 时自动执行,将报文缓存到内部接收帧。
*/
extern "C" void CAN0_RX0_IRQHandler(void)
{
if(can_interrupt_flag_get(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0) == RESET) {
return;
}
can_receive_message_struct rx_message{};
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message);
can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message);
g_can_diag.rx_count++;
g_can_diag.last_payload_length = rx_message.rx_dlen;
g_can_diag.last_rx_fd_flag = rx_message.fd_flag;
g_can_diag.last_rx_brs = rx_message.fd_brs;
g_can_diag.last_rx_esi = rx_message.fd_esi;
g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid;
g_rx_frame.id = g_can_diag.last_rx_id;
g_rx_frame.length = rx_message.rx_dlen;
for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) {
g_rx_frame.data[i] = rx_message.rx_data[i];
}
g_rx_frame.valid = 1U;
can_interrupt_flag_clear(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0);
}
/**
* @brief 初始化 CANFD 外设、GPIO、波特率参数、接收过滤器与中断。
* @note 启用 FIFO0 RX 中断替代轮询,TX 保持 mailbox 异步发送;GPIO 与原理图一致使用 PA11/PA12/PA8。
* 其中 PA8 已封装为正常/静默两个模式控制函数,初始化时默认进入 Normal 模式。
*/
void canfd_init(void)
{
can_gpio_init();
can_transceiver_normal_mode();
can_deinit(CONFIG_CAN_INSTANCE);
can_nominal_init();
can_fd_mode_init();
can_filter_accept_all();
/* 使能 FIFO0 接收中断,替代轮询 */
can_interrupt_enable(CONFIG_CAN_INSTANCE, CAN_INT_RFNE0);
nvic_irq_enable(CAN0_RX0_IRQn, 1U, 1U);
g_rx_frame.valid = 0U;
g_tx_buffer.valid = 0U;
}
/**
* @brief 轮询 CANFD 接收 FIFO(中断模式下退化为兼容空壳)。
* @note 实际接收已由 CAN0_RX0_IRQHandler 以中断方式完成,此函数保留接口不破坏现有调用链。
*/
void canfd_poll(void)
{
/* 接收已由 CAN0_RX0_IRQHandler 中断驱动 */
}
/**
* @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。
* @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。
*/
void canfd_prepare_status(const canfd_status_payload_t *status)
{
if(status == nullptr) {
g_tx_buffer.valid = 0U;
return;
}
canfd_build_status_frame(status);
}
/**
* @brief 发送已准备好的 CANFD 状态报文。
* @note 若当前没有有效待发送报文,直接返回。
*/
void canfd_send_prepared(void)
{
if(g_tx_buffer.valid == 0U) {
return;
}
const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message);
if(mailbox != CAN_NOMAILBOX) {
g_can_diag.tx_count++;
g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen;
g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid;
} else {
g_can_diag.error_count++;
}
}
/**
* @brief 查询是否存在尚未消费的接收报文。
* @retval 非 0 存在待消费报文,0 无报文。
*/
uint8_t canfd_has_rx_frame(void)
{
return g_rx_frame.valid;
}
/**
* @brief 读取并消费最近一次缓存的接收报文。
* @param[out] id 输出报文标识符的指针;允许为空。
* @param[out] data 输出报文数据缓存区指针;允许为空。
* @param[out] length 输出报文数据长度的指针;允许为空。
* @retval 1 成功消费一帧报文,0 当前无有效缓存报文。
*/
uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length)
{
if(g_rx_frame.valid == 0U) {
return 0U;
}
if(id != nullptr) {
*id = g_rx_frame.id;
}
if(length != nullptr) {
*length = g_rx_frame.length;
}
if(data != nullptr) {
for(uint8_t i = 0; i < g_rx_frame.length; ++i) {
data[i] = g_rx_frame.data[i];
}
}
g_rx_frame.valid = 0U;
return 1U;
}
/**
* @brief 获取 CANFD 诊断统计信息只读指针。
* @retval 指向内部诊断统计结构体的只读指针。
*/
const canfd_diag_t *canfd_get_diag(void)
{
return &g_can_diag;
}
@@ -0,0 +1,319 @@
/**
* @file CANFD.cpp
* @brief CANFD 通信外设初始化、收发与诊断实现。
*/
#include "main.h"
#include "device/canfd.h"
#include "sys/config.h"
namespace {
constexpr uint32_t kCanSlpGpio = GPIOA;
constexpr uint32_t kCanSlpPin = GPIO_PIN_8;
/** @brief 内部接收帧缓存结构。 */
struct CanfdRxFrame {
uint32_t id = 0U;
uint8_t data[64] = {0};
uint8_t length = 0U;
uint8_t valid = 0U;
};
/** @brief 内部发送帧缓存结构。 */
struct CanfdTxBuffer {
can_trasnmit_message_struct tx_message{};
uint8_t payload[64] = {0};
uint8_t valid = 0U;
};
canfd_diag_t g_can_diag{};
CanfdRxFrame g_rx_frame{};
CanfdTxBuffer g_tx_buffer{};
/**
* @brief 初始化 CAN0 外设所需 GPIO 引脚。
* @note 按原理图连接关系配置:
* - PA12 -> CAN0_TX
* - PA11 -> CAN0_RX
* - PA8 -> CAN0_SLP(连接 TCAN1057A #0 模式控制脚)
*/
inline void can_gpio_init()
{
rcu_periph_clock_enable(CONFIG_CAN_RCU);
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_11);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);
gpio_init(kCanSlpGpio, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, kCanSlpPin);
}
/**
* @brief 将 TCAN1057A #0 置为正常工作模式。
* @note 结合 TCAN1057A-Q1 产品资料:器件工作模式为 Normal / Silent,
* 当前原理图网络名虽然写作 CAN0_SLP,但本质是模式控制脚;
* 这里按 STB=0 -> Normal,STB=1 -> Silent 处理,因此 PA8 拉低进入正常模式。
*/
inline void can_transceiver_normal_mode()
{
gpio_bit_reset(kCanSlpGpio, kCanSlpPin);
}
/**
* @brief 配置 CAN 接收过滤器为接受所有报文。
*/
inline void can_filter_accept_all()
{
can_filter_parameter_struct can_filter{};
can_filter.filter_number = 0U;
can_filter.filter_mode = CAN_FILTERMODE_MASK;
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
can_filter.filter_list_high = 0x0000U;
can_filter.filter_list_low = 0x0000U;
can_filter.filter_mask_high = 0x0000U;
can_filter.filter_mask_low = 0x0000U;
can_filter.filter_fifo_number = CAN_FIFO0;
can_filter.filter_enable = ENABLE;
can_filter_init(CONFIG_CAN_INSTANCE, &can_filter);
}
/**
* @brief 配置 CAN 标称位定时参数。
* @note 参考 APP 项目配置:仲裁段波特率为 1 Mbps,采样点约 80%。
*/
inline void can_nominal_init()
{
can_parameter_struct can_parameter{};
can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
can_parameter.time_triggered = DISABLE;
can_parameter.auto_bus_off_recovery = ENABLE;
can_parameter.auto_wake_up = DISABLE;
can_parameter.auto_retrans = ENABLE;
can_parameter.rec_fifo_overwrite = ENABLE;
can_parameter.trans_fifo_order = ENABLE;
can_parameter.working_mode = CAN_NORMAL_MODE;
can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
can_parameter.time_segment_1 = CAN_BT_BS1_7TQ;
can_parameter.time_segment_2 = CAN_BT_BS2_2TQ;
can_parameter.prescaler = 6U;
can_init(CONFIG_CAN_INSTANCE, &can_parameter);
}
/**
* @brief 配置 CANFD 可变速率模式参数。
* @note 参考 APP 项目配置:
* - 仲裁段保持 1 Mbps;
* - 数据段配置为 2 Mbps,采样点约 80%;
* - 发送时启用 BRS 后才会切到数据段速率。
*/
inline void can_fd_mode_init()
{
#ifdef GD32E508
can_fdframe_struct can_fd_parameter{};
can_fd_tdc_struct can_fd_tdc_parameter{};
can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter);
can_fd_parameter.fd_frame = ENABLE;
can_fd_parameter.excp_event_detect = ENABLE;
can_fd_parameter.delay_compensation = ENABLE;
can_fd_tdc_parameter.tdc_filter = 0x04U;
can_fd_tdc_parameter.tdc_mode = CAN_TDCMOD_CALC_AND_OFFSET;
can_fd_tdc_parameter.tdc_offset = 0x04U;
can_fd_parameter.p_delay_compensation = &can_fd_tdc_parameter;
can_fd_parameter.iso_bosch = CAN_FDMOD_ISO;
can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE;
can_fd_parameter.data_prescaler = 6U;
can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ;
can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ;
can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ;
can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter);
#else
can_fd_function_enable(CONFIG_CAN_INSTANCE);
#endif
}
/**
* @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。
* @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。
*/
inline void canfd_build_status_frame(const canfd_status_payload_t *status)
{
const int16_t temp_x100 = static_cast<int16_t>(status->temperature * 100.0f);
const uint16_t vdda_x1000 = static_cast<uint16_t>(status->vdda * 1000.0f);
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message);
g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID;
g_tx_buffer.tx_message.tx_efid = 0U;
g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD;
g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA;
g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN;
#ifdef GD32E508
g_tx_buffer.tx_message.fd_flag = ENABLE;
g_tx_buffer.tx_message.fd_brs = ENABLE;
g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT;
#endif
auto &p = g_tx_buffer.payload;
p[0] = static_cast<uint8_t>(status->temp_raw & 0xFFU);
p[1] = static_cast<uint8_t>((status->temp_raw >> 8) & 0xFFU);
p[2] = static_cast<uint8_t>(status->vref_raw & 0xFFU);
p[3] = static_cast<uint8_t>((status->vref_raw >> 8) & 0xFFU);
p[4] = static_cast<uint8_t>(temp_x100 & 0xFF);
p[5] = static_cast<uint8_t>((temp_x100 >> 8) & 0xFF);
p[6] = static_cast<uint8_t>(vdda_x1000 & 0xFFU);
p[7] = static_cast<uint8_t>((vdda_x1000 >> 8) & 0xFFU);
p[8] = CONFIG_CAN_STATUS_TAG0;
p[9] = CONFIG_CAN_STATUS_TAG1;
p[10] = CONFIG_CAN_STATUS_TAG2;
p[11] = CONFIG_CAN_STATUS_TAG3;
for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) {
g_tx_buffer.tx_message.tx_data[i] = p[i];
}
g_tx_buffer.valid = 1U;
}
} // namespace
/**
* @brief CAN0 RX0 中断服务例程。
* @note 在 CANFD 帧到达 FIFO0 时自动执行,将报文缓存到内部接收帧。
*/
extern "C" void CAN0_RX0_IRQHandler(void)
{
if(can_interrupt_flag_get(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0) == RESET) {
return;
}
can_receive_message_struct rx_message{};
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message);
can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message);
g_can_diag.rx_count++;
g_can_diag.last_payload_length = rx_message.rx_dlen;
g_can_diag.last_rx_fd_flag = rx_message.fd_flag;
g_can_diag.last_rx_brs = rx_message.fd_brs;
g_can_diag.last_rx_esi = rx_message.fd_esi;
g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid;
g_rx_frame.id = g_can_diag.last_rx_id;
g_rx_frame.length = rx_message.rx_dlen;
for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) {
g_rx_frame.data[i] = rx_message.rx_data[i];
}
g_rx_frame.valid = 1U;
can_interrupt_flag_clear(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0);
}
/**
* @brief 初始化 CANFD 外设、GPIO、波特率参数、接收过滤器与中断。
* @note 启用 FIFO0 RX 中断替代轮询,TX 保持 mailbox 异步发送;GPIO 与原理图一致使用 PA11/PA12/PA8。
* 其中 PA8 按 TCAN1057A 的模式控制脚处理:低电平为 Normal,高电平为 Silent。
*/
void canfd_init(void)
{
can_gpio_init();
can_transceiver_normal_mode();
can_deinit(CONFIG_CAN_INSTANCE);
can_nominal_init();
can_fd_mode_init();
can_filter_accept_all();
/* 使能 FIFO0 接收中断,替代轮询 */
can_interrupt_enable(CONFIG_CAN_INSTANCE, CAN_INT_RFNE0);
nvic_irq_enable(CAN0_RX0_IRQn, 1U, 1U);
g_rx_frame.valid = 0U;
g_tx_buffer.valid = 0U;
}
/**
* @brief 轮询 CANFD 接收 FIFO(中断模式下退化为兼容空壳)。
* @note 实际接收已由 CAN0_RX0_IRQHandler 以中断方式完成,此函数保留接口不破坏现有调用链。
*/
void canfd_poll(void)
{
/* 接收已由 CAN0_RX0_IRQHandler 中断驱动 */
}
/**
* @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。
* @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。
*/
void canfd_prepare_status(const canfd_status_payload_t *status)
{
if(status == nullptr) {
g_tx_buffer.valid = 0U;
return;
}
canfd_build_status_frame(status);
}
/**
* @brief 发送已准备好的 CANFD 状态报文。
* @note 若当前没有有效待发送报文,直接返回。
*/
void canfd_send_prepared(void)
{
if(g_tx_buffer.valid == 0U) {
return;
}
const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message);
if(mailbox != CAN_NOMAILBOX) {
g_can_diag.tx_count++;
g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen;
g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid;
} else {
g_can_diag.error_count++;
}
}
/**
* @brief 查询是否存在尚未消费的接收报文。
* @retval 非 0 存在待消费报文,0 无报文。
*/
uint8_t canfd_has_rx_frame(void)
{
return g_rx_frame.valid;
}
/**
* @brief 读取并消费最近一次缓存的接收报文。
* @param[out] id 输出报文标识符的指针;允许为空。
* @param[out] data 输出报文数据缓存区指针;允许为空。
* @param[out] length 输出报文数据长度的指针;允许为空。
* @retval 1 成功消费一帧报文,0 当前无有效缓存报文。
*/
uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length)
{
if(g_rx_frame.valid == 0U) {
return 0U;
}
if(id != nullptr) {
*id = g_rx_frame.id;
}
if(length != nullptr) {
*length = g_rx_frame.length;
}
if(data != nullptr) {
for(uint8_t i = 0; i < g_rx_frame.length; ++i) {
data[i] = g_rx_frame.data[i];
}
}
g_rx_frame.valid = 0U;
return 1U;
}
/**
* @brief 获取 CANFD 诊断统计信息只读指针。
* @retval 指向内部诊断统计结构体的只读指针。
*/
const canfd_diag_t *canfd_get_diag(void)
{
return &g_can_diag;
}
+300
View File
@@ -0,0 +1,300 @@
/**
* @file CANFD.cpp
* @brief CANFD 通信外设初始化、收发与诊断实现。
*/
#include "main.h"
#include "device/canfd.h"
#include "sys/config.h"
namespace {
/** @brief 内部接收帧缓存结构。 */
struct CanfdRxFrame {
uint32_t id = 0U;
uint8_t data[64] = {0};
uint8_t length = 0U;
uint8_t valid = 0U;
};
/** @brief 内部发送帧缓存结构。 */
struct CanfdTxBuffer {
can_trasnmit_message_struct tx_message{};
uint8_t payload[64] = {0};
uint8_t valid = 0U;
};
canfd_diag_t g_can_diag{};
CanfdRxFrame g_rx_frame{};
CanfdTxBuffer g_tx_buffer{};
/**
* @brief 初始化 CAN 外设所需 GPIO 引脚。
*/
inline void can_gpio_init()
{
rcu_periph_clock_enable(CONFIG_CAN_RCU);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_pin_remap_config(GPIO_CAN0_PARTIAL_REMAP, ENABLE);
}
/**
* @brief 配置 CAN 接收过滤器为接受所有报文。
*/
inline void can_filter_accept_all()
{
can_filter_parameter_struct can_filter{};
can_filter.filter_number = 0U;
can_filter.filter_mode = CAN_FILTERMODE_MASK;
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
can_filter.filter_list_high = 0x0000U;
can_filter.filter_list_low = 0x0000U;
can_filter.filter_mask_high = 0x0000U;
can_filter.filter_mask_low = 0x0000U;
can_filter.filter_fifo_number = CAN_FIFO0;
can_filter.filter_enable = ENABLE;
can_filter_init(CONFIG_CAN_INSTANCE, &can_filter);
}
/**
* @brief 配置 CAN 标称位定时参数。
* @note 参考 APP 项目配置:仲裁段波特率为 1 Mbps,采样点约 80%。
*/
inline void can_nominal_init()
{
can_parameter_struct can_parameter{};
can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
can_parameter.time_triggered = DISABLE;
can_parameter.auto_bus_off_recovery = ENABLE;
can_parameter.auto_wake_up = DISABLE;
can_parameter.auto_retrans = ENABLE;
can_parameter.rec_fifo_overwrite = ENABLE;
can_parameter.trans_fifo_order = ENABLE;
can_parameter.working_mode = CAN_NORMAL_MODE;
can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
can_parameter.time_segment_1 = CAN_BT_BS1_7TQ;
can_parameter.time_segment_2 = CAN_BT_BS2_2TQ;
can_parameter.prescaler = 6U;
can_init(CONFIG_CAN_INSTANCE, &can_parameter);
}
/**
* @brief 配置 CANFD 可变速率模式参数。
* @note 参考 APP 项目配置:
* - 仲裁段保持 1 Mbps;
* - 数据段配置为 2 Mbps,采样点约 80%;
* - 发送时启用 BRS 后才会切到数据段速率。
*/
inline void can_fd_mode_init()
{
#ifdef GD32E508
can_fdframe_struct can_fd_parameter{};
can_fd_tdc_struct can_fd_tdc_parameter{};
can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter);
can_fd_parameter.fd_frame = ENABLE;
can_fd_parameter.excp_event_detect = ENABLE;
can_fd_parameter.delay_compensation = ENABLE;
can_fd_tdc_parameter.tdc_filter = 0x04U;
can_fd_tdc_parameter.tdc_mode = CAN_TDCMOD_CALC_AND_OFFSET;
can_fd_tdc_parameter.tdc_offset = 0x04U;
can_fd_parameter.p_delay_compensation = &can_fd_tdc_parameter;
can_fd_parameter.iso_bosch = CAN_FDMOD_ISO;
can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE;
can_fd_parameter.data_prescaler = 6U;
can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ;
can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ;
can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ;
can_fd_init(CONFIG_CAN_INSTANCE, &can_fd_parameter);
#else
can_fd_function_enable(CONFIG_CAN_INSTANCE);
#endif
}
/**
* @brief 根据状态载荷构造 CANFD 状态发送帧并置有效标志。
* @param[in] status 指向状态载荷的指针;不为空时执行构造和置位。
*/
inline void canfd_build_status_frame(const canfd_status_payload_t *status)
{
const int16_t temp_x100 = static_cast<int16_t>(status->temperature * 100.0f);
const uint16_t vdda_x1000 = static_cast<uint16_t>(status->vdda * 1000.0f);
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &g_tx_buffer.tx_message);
g_tx_buffer.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID;
g_tx_buffer.tx_message.tx_efid = 0U;
g_tx_buffer.tx_message.tx_ff = CAN_FF_STANDARD;
g_tx_buffer.tx_message.tx_ft = CAN_FT_DATA;
g_tx_buffer.tx_message.tx_dlen = CANFD_STATUS_DLEN;
#ifdef GD32E508
g_tx_buffer.tx_message.fd_flag = ENABLE;
g_tx_buffer.tx_message.fd_brs = ENABLE;
g_tx_buffer.tx_message.fd_esi = CAN_ESI_DOMINANT;
#endif
auto &p = g_tx_buffer.payload;
p[0] = static_cast<uint8_t>(status->temp_raw & 0xFFU);
p[1] = static_cast<uint8_t>((status->temp_raw >> 8) & 0xFFU);
p[2] = static_cast<uint8_t>(status->vref_raw & 0xFFU);
p[3] = static_cast<uint8_t>((status->vref_raw >> 8) & 0xFFU);
p[4] = static_cast<uint8_t>(temp_x100 & 0xFF);
p[5] = static_cast<uint8_t>((temp_x100 >> 8) & 0xFF);
p[6] = static_cast<uint8_t>(vdda_x1000 & 0xFFU);
p[7] = static_cast<uint8_t>((vdda_x1000 >> 8) & 0xFFU);
p[8] = CONFIG_CAN_STATUS_TAG0;
p[9] = CONFIG_CAN_STATUS_TAG1;
p[10] = CONFIG_CAN_STATUS_TAG2;
p[11] = CONFIG_CAN_STATUS_TAG3;
for(uint8_t i = 0; i < CANFD_STATUS_DLEN; ++i) {
g_tx_buffer.tx_message.tx_data[i] = p[i];
}
g_tx_buffer.valid = 1U;
}
} // namespace
/**
* @brief CAN0 RX0 中断服务例程。
* @note 在 CANFD 帧到达 FIFO0 时自动执行,将报文缓存到内部接收帧。
*/
extern "C" void CAN0_RX0_IRQHandler(void)
{
if(can_interrupt_flag_get(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0) == RESET) {
return;
}
can_receive_message_struct rx_message{};
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message);
can_message_receive(CONFIG_CAN_INSTANCE, CAN_FIFO0, &rx_message);
g_can_diag.rx_count++;
g_can_diag.last_payload_length = rx_message.rx_dlen;
g_can_diag.last_rx_fd_flag = rx_message.fd_flag;
g_can_diag.last_rx_brs = rx_message.fd_brs;
g_can_diag.last_rx_esi = rx_message.fd_esi;
g_can_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid;
g_rx_frame.id = g_can_diag.last_rx_id;
g_rx_frame.length = rx_message.rx_dlen;
for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) {
g_rx_frame.data[i] = rx_message.rx_data[i];
}
g_rx_frame.valid = 1U;
can_interrupt_flag_clear(CONFIG_CAN_INSTANCE, CAN_INT_FLAG_RFL0);
}
/**
* @brief 初始化 CANFD 外设、GPIO、波特率参数、接收过滤器与中断。
* @note 启用 FIFO0 RX 中断替代轮询,TX 保持 mailbox 异步发送。
*/
void canfd_init(void)
{
can_gpio_init();
can_deinit(CONFIG_CAN_INSTANCE);
can_nominal_init();
can_fd_mode_init();
can_filter_accept_all();
/* 使能 FIFO0 接收中断,替代轮询 */
can_interrupt_enable(CONFIG_CAN_INSTANCE, CAN_INT_RFNE0);
nvic_irq_enable(CAN0_RX0_IRQn, 1U, 1U);
g_rx_frame.valid = 0U;
g_tx_buffer.valid = 0U;
}
/**
* @brief 轮询 CANFD 接收 FIFO(中断模式下退化为兼容空壳)。
* @note 实际接收已由 CAN0_RX0_IRQHandler 以中断方式完成,此函数保留接口不破坏现有调用链。
*/
void canfd_poll(void)
{
/* 接收已由 CAN0_RX0_IRQHandler 中断驱动 */
}
/**
* @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。
* @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。
*/
void canfd_prepare_status(const canfd_status_payload_t *status)
{
if(status == nullptr) {
g_tx_buffer.valid = 0U;
return;
}
canfd_build_status_frame(status);
}
/**
* @brief 发送已准备好的 CANFD 状态报文。
* @note 若当前没有有效待发送报文,直接返回。
*/
void canfd_send_prepared(void)
{
if(g_tx_buffer.valid == 0U) {
return;
}
const uint8_t mailbox = can_message_transmit(CONFIG_CAN_INSTANCE, &g_tx_buffer.tx_message);
if(mailbox != CAN_NOMAILBOX) {
g_can_diag.tx_count++;
g_can_diag.last_payload_length = g_tx_buffer.tx_message.tx_dlen;
g_can_diag.last_tx_id = g_tx_buffer.tx_message.tx_sfid;
} else {
g_can_diag.error_count++;
}
}
/**
* @brief 查询是否存在尚未消费的接收报文。
* @retval 非 0 存在待消费报文,0 无报文。
*/
uint8_t canfd_has_rx_frame(void)
{
return g_rx_frame.valid;
}
/**
* @brief 读取并消费最近一次缓存的接收报文。
* @param[out] id 输出报文标识符的指针;允许为空。
* @param[out] data 输出报文数据缓存区指针;允许为空。
* @param[out] length 输出报文数据长度的指针;允许为空。
* @retval 1 成功消费一帧报文,0 当前无有效缓存报文。
*/
uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length)
{
if(g_rx_frame.valid == 0U) {
return 0U;
}
if(id != nullptr) {
*id = g_rx_frame.id;
}
if(length != nullptr) {
*length = g_rx_frame.length;
}
if(data != nullptr) {
for(uint8_t i = 0; i < g_rx_frame.length; ++i) {
data[i] = g_rx_frame.data[i];
}
}
g_rx_frame.valid = 0U;
return 1U;
}
/**
* @brief 获取 CANFD 诊断统计信息只读指针。
* @retval 指向内部诊断统计结构体的只读指针。
*/
const canfd_diag_t *canfd_get_diag(void)
{
return &g_can_diag;
}
+51
View File
@@ -0,0 +1,51 @@
/**
* @file Timer.cpp
* @brief 用户调度定时器模块实现。
*/
#include "main.h"
#include "device/Timer.h"
#include "sys/task_manager.h"
#include "sys/config.h"
void SchedulerTimer::configureTimer()
{
timer_parameter_struct timer_initpara;
rcu_periph_clock_enable(CONFIG_SCHED_TIMER_RCU);
timer_deinit(CONFIG_SCHED_TIMER_INSTANCE);
timer_struct_para_init(&timer_initpara);
timer_initpara.prescaler = CONFIG_SCHED_TIMER_PRESCALER;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = CONFIG_SCHED_TIMER_PERIOD;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_init(CONFIG_SCHED_TIMER_INSTANCE, &timer_initpara);
timer_interrupt_flag_clear(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP);
timer_interrupt_enable(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_UP);
timer_enable(CONFIG_SCHED_TIMER_INSTANCE);
}
void SchedulerTimer::configureNvic()
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
nvic_irq_enable(CONFIG_SCHED_TIMER_IRQn, CONFIG_SCHED_TIMER_IRQ_PREEMPT_PRIO, CONFIG_SCHED_TIMER_IRQ_SUB_PRIO);
}
void SchedulerTimer::init()
{
configureTimer();
configureNvic();
}
void SchedulerTimer::onInterrupt()
{
if(timer_interrupt_flag_get(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP) == RESET) return;
timer_interrupt_flag_clear(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP);
TaskManager::tick100us();
}
/* ----- 中断入口 C 包装 ---------------------------------------------- */
void TIMER2_IRQHandler(void) { SchedulerTimer::onInterrupt(); }
+65
View File
@@ -0,0 +1,65 @@
/**
* @file Timer.cpp
* @brief 用户定时器初始化与中断处理实现。
*/
#include "main.h"
#include "device/Timer.h"
#include "sys/task_manager.h"
#include "sys/config.h"
/**
* @brief 配置定时器 2 参数:180MHz / 180 / 100 → 100us 中断周期。
*/
static void timer2_config(void)
{
timer_parameter_struct timer_initpara;
rcu_periph_clock_enable(CONFIG_SCHED_TIMER_RCU);
timer_deinit(CONFIG_SCHED_TIMER_INSTANCE);
timer_struct_para_init(&timer_initpara);
timer_initpara.prescaler = CONFIG_SCHED_TIMER_PRESCALER;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = CONFIG_SCHED_TIMER_PERIOD;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_init(CONFIG_SCHED_TIMER_INSTANCE, &timer_initpara);
timer_interrupt_flag_clear(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP);
timer_interrupt_enable(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_UP);
timer_enable(CONFIG_SCHED_TIMER_INSTANCE);
}
/**
* @brief 配置定时器 2 的中断优先级并使能中断。
*/
static void timer2_nvic_config(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
nvic_irq_enable(CONFIG_SCHED_TIMER_IRQn, CONFIG_SCHED_TIMER_IRQ_PREEMPT_PRIO, CONFIG_SCHED_TIMER_IRQ_SUB_PRIO);
}
/**
* @brief 初始化用户定时器及其中断配置。
* @note 当前用于建立 100us 基准节拍,驱动任务调度器时基。
*/
void user_timer_init(void)
{
timer2_config();
timer2_nvic_config();
}
/**
* @brief 定时器 2 中断服务例程。
* @note 每次计数溢出时清除标志并推进一次 100us 任务调度节拍。
*/
void TIMER2_IRQHandler(void)
{
if(timer_interrupt_flag_get(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP) == RESET) {
return;
}
timer_interrupt_flag_clear(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP);
task_manager_tick_100us();
}
+65
View File
@@ -0,0 +1,65 @@
/**
* @file Timer.cpp
* @brief 用户定时器初始化与中断处理实现。
*/
#include "main.h"
#include "device/Timer.h"
#include "sys/task_manager.h"
#include "sys/config.h"
/**
* @brief 配置定时器 2 参数:180MHz / 180 / 100 → 100us 中断周期。
*/
static void timer2_config(void)
{
timer_parameter_struct timer_initpara;
rcu_periph_clock_enable(CONFIG_SCHED_TIMER_RCU);
timer_deinit(CONFIG_SCHED_TIMER_INSTANCE);
timer_struct_para_init(&timer_initpara);
timer_initpara.prescaler = CONFIG_SCHED_TIMER_PRESCALER;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = CONFIG_SCHED_TIMER_PERIOD;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_init(CONFIG_SCHED_TIMER_INSTANCE, &timer_initpara);
timer_interrupt_flag_clear(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP);
timer_interrupt_enable(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_UP);
timer_enable(CONFIG_SCHED_TIMER_INSTANCE);
}
/**
* @brief 配置定时器 2 的中断优先级并使能中断。
*/
static void timer2_nvic_config(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
nvic_irq_enable(CONFIG_SCHED_TIMER_IRQn, CONFIG_SCHED_TIMER_IRQ_PREEMPT_PRIO, CONFIG_SCHED_TIMER_IRQ_SUB_PRIO);
}
/**
* @brief 初始化用户定时器及其中断配置。
* @note 当前用于建立 100us 基准节拍,驱动任务调度器时基。
*/
void user_timer_init(void)
{
timer2_config();
timer2_nvic_config();
}
/**
* @brief 定时器 2 中断服务例程。
* @note 每次计数溢出时清除标志并推进一次 100us 任务调度节拍。
*/
void TIMER2_IRQHandler(void)
{
if(timer_interrupt_flag_get(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP) == RESET) {
return;
}
timer_interrupt_flag_clear(CONFIG_SCHED_TIMER_INSTANCE, TIMER_INT_FLAG_UP);
task_manager_tick_100us();
}
+18
View File
@@ -0,0 +1,18 @@
#ifndef DEVICE_TIMER_H
#define DEVICE_TIMER_H
/**
* @brief 用户调度定时器模块。
* @note 负责建立 100us 基准节拍,并通过中断驱动任务调度器。
*/
class SchedulerTimer {
public:
static void init();
static void onInterrupt();
private:
static void configureTimer();
static void configureNvic();
};
#endif /* DEVICE_TIMER_H */
+17
View File
@@ -0,0 +1,17 @@
/**
* @file Timer.h
* @brief 用户定时器初始化接口定义。
*/
#ifndef DEVICE_TIMER_H
#define DEVICE_TIMER_H
/**
* @brief 初始化用户定时器及其中断配置。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 当前用于建立 100us 基准节拍,驱动任务调度器时基。
*/
void user_timer_init(void);
#endif /* DEVICE_TIMER_H */
+43
View File
@@ -0,0 +1,43 @@
#ifndef DEVICE_ADC_H
#define DEVICE_ADC_H
#include <stdint.h>
/**
* @brief ADC 采样模块。
* @note 负责 VIN_ADC、内部温度与内部参考电压的 DMA 扫描采样与工程值换算。
*/
class Adc {
public:
/**
* @brief ADC 采样结果结构体。
* @note 保存外部 VIN_ADC、内部温度与内部参考电压的原始值,以及可直接使用的工程值。
*/
struct Sample {
uint16_t vin_raw; /**< @brief 外部 VIN_ADC(PA6)原始 ADC 数值。 */
uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */
uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */
float vin_adc_v; /**< @brief VIN_ADC 引脚处电压,单位 V(仅为分压后的 ADC 端电压)。 */
float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */
float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */
};
static void init();
static bool dmaReady();
static const uint16_t *dmaBuffer();
static uint16_t readRaw(uint8_t channel);
static uint16_t readVinRaw();
static uint16_t readVrefRaw();
static uint16_t readTempRaw();
static float calculateVinAdcVoltage(uint16_t vinRaw, float vdda);
static float calculateVdda(uint16_t vrefRaw);
static float calculateTemperature(uint16_t tempRaw, float vdda);
static void collectSample(Sample *sample);
private:
static void initDma();
};
using adc_sample_t = Adc::Sample;
#endif /* DEVICE_ADC_H */
+78
View File
@@ -0,0 +1,78 @@
#ifndef DEVICE_ADC_H
#define DEVICE_ADC_H
#include <stdint.h>
/**
* @brief ADC 采样结果结构体。
* @note 同时保存内部温度与内部参考电压的原始值及换算后的工程值。
*/
typedef struct {
uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */
uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */
float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */
float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */
} adc_sample_t;
/**
* @brief 初始化 ADC 硬件及内部温度/VREF 通道。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 该函数完成 ADC 时钟、模式、校准以及内部通道使能。
*/
void adc_hw_init(void);
/**
* @brief 执行指定通道的一次 ADC 单次采样。
* @param[in] channel 待采样的 ADC 通道号。
* @param[out] 无。
* @retval uint16_t 指定通道的原始 ADC 采样值。
* @note 函数内部会阻塞等待本次转换完成。
*/
uint16_t adc_single_read(uint8_t channel);
/**
* @brief 读取内部参考电压通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部参考电压通道的原始 ADC 值。
*/
uint16_t adc_read_vref_raw(void);
/**
* @brief 读取内部温度通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部温度通道的原始 ADC 值。
*/
uint16_t adc_read_temp_raw(void);
/**
* @brief 根据内部参考电压原始值计算实际 VDDA。
* @param[in] vref_raw 内部参考电压通道原始 ADC 数值。
* @param[out] 无。
* @retval float 换算得到的 VDDA 电压值,单位 V。
* @note 当输入为 0 时返回默认 3.3V,避免除零异常。
*/
float adc_calculate_vdda(uint16_t vref_raw);
/**
* @brief 根据温度原始值与 VDDA 计算芯片温度。
* @param[in] temp_raw 内部温度通道原始 ADC 数值。
* @param[in] vdda 当前供电电压,单位 V。
* @param[out] 无。
* @retval float 换算得到的芯片温度,单位 ℃。
*/
float adc_calculate_temperature(uint16_t temp_raw, float vdda);
/**
* @brief 采集一组完整 ADC 样本并完成工程值换算。
* @param[in] sample 指向输出样本结构体的指针;允许为空。
* @param[out] sample 当指针非空时,写入温度/VREF 原始值与换算结果。
* @retval 无返回值。
* @note 当 sample 为 NULL 时函数直接返回,不执行写操作。
*/
void adc_collect_sample(adc_sample_t *sample);
#endif /* DEVICE_ADC_H */
+114
View File
@@ -0,0 +1,114 @@
#ifndef DEVICE_ADC_H
#define DEVICE_ADC_H
#include <stdint.h>
/**
* @brief ADC 采样结果结构体。
* @note 保存外部 VIN_ADC、内部温度与内部参考电压的原始值,以及可直接使用的工程值。
*/
typedef struct {
uint16_t vin_raw; /**< @brief 外部 VIN_ADC(PA6)原始 ADC 数值。 */
uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */
uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */
float vin_adc_v; /**< @brief VIN_ADC 引脚处电压,单位 V(仅为分压后的 ADC 端电压)。 */
float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */
float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */
} adc_sample_t;
/**
* @brief 初始化 ADC 硬件及内部温度/VREF/VIN_ADC 通道。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 该函数完成 ADC 时钟、三通道扫描、DMA 循环缓存以及内部/外部采样通道使能。
*/
void adc_hw_init(void);
/**
* @brief 查询 ADC DMA 循环缓存是否已完成首次有效填充。
* @param[in] 无。
* @param[out] 无。
* @retval uint8_t 返回非 0 表示缓存已可用,返回 0 表示仍未准备好。
*/
uint8_t adc_dma_ready(void);
/**
* @brief 获取 ADC DMA 原始循环缓存只读指针。
* @param[in] 无。
* @param[out] 无。
* @retval const uint16_t* 返回长度为 3 的原始缓存指针,顺序为 VIN_ADC、温度通道、VREF 通道。
*/
const uint16_t *adc_dma_buffer_get(void);
/**
* @brief 执行指定通道的一次 ADC 单次采样。
* @param[in] channel 待采样的 ADC 通道号。
* @param[out] 无。
* @retval uint16_t 指定通道的原始 ADC 采样值。
* @note 在 DMA 缓存模式下,该函数优先返回已缓存的对应通道原始值;若通道不受支持则返回 0。
*/
uint16_t adc_single_read(uint8_t channel);
/**
* @brief 读取外部 VIN_ADC(PA6)通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 外部 VIN_ADC 通道的原始 ADC 值。
*/
uint16_t adc_read_vin_raw(void);
/**
* @brief 读取内部参考电压通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部参考电压通道的原始 ADC 值。
*/
uint16_t adc_read_vref_raw(void);
/**
* @brief 读取内部温度通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部温度通道的原始 ADC 值。
*/
uint16_t adc_read_temp_raw(void);
/**
* @brief 根据外部 VIN_ADC 原始值与 VDDA 计算 ADC 引脚处电压。
* @param[in] vin_raw 外部 VIN_ADC 通道原始 ADC 数值。
* @param[in] vdda 当前供电电压,单位 V。
* @param[out] 无。
* @retval float VIN_ADC 引脚电压,单位 V。
* @note 当前仅计算分压后的 ADC 端电压;若需换算输入母线电压,需要补充完整分压比。
*/
float adc_calculate_vin_adc_voltage(uint16_t vin_raw, float vdda);
/**
* @brief 根据内部参考电压原始值计算实际 VDDA。
* @param[in] vref_raw 内部参考电压通道原始 ADC 数值。
* @param[out] 无。
* @retval float 换算得到的 VDDA 电压值,单位 V。
* @note 当输入为 0 时返回默认 3.3V,避免除零异常。
*/
float adc_calculate_vdda(uint16_t vref_raw);
/**
* @brief 根据温度原始值与 VDDA 计算芯片温度。
* @param[in] temp_raw 内部温度通道原始 ADC 数值。
* @param[in] vdda 当前供电电压,单位 V。
* @param[out] 无。
* @retval float 换算得到的芯片温度,单位 ℃。
*/
float adc_calculate_temperature(uint16_t temp_raw, float vdda);
/**
* @brief 采集一组完整 ADC 样本并完成工程值换算。
* @param[in] sample 指向输出样本结构体的指针;允许为空。
* @param[out] sample 当指针非空时,写入 VIN/温度/VREF 原始值与换算结果。
* @retval 无返回值。
* @note 当 sample 为 NULL 时函数直接返回,不执行写操作。
*/
void adc_collect_sample(adc_sample_t *sample);
#endif /* DEVICE_ADC_H */
+94
View File
@@ -0,0 +1,94 @@
#ifndef DEVICE_ADC_H
#define DEVICE_ADC_H
#include <stdint.h>
/**
* @brief ADC 采样结果结构体。
* @note 同时保存内部温度与内部参考电压的原始值及换算后的工程值。
*/
typedef struct {
uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */
uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */
float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */
float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */
} adc_sample_t;
/**
* @brief 初始化 ADC 硬件及内部温度/VREF 通道。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 该函数完成 ADC 时钟、双通道扫描、DMA 循环缓存以及内部通道使能。
*/
void adc_hw_init(void);
/**
* @brief 查询 ADC DMA 循环缓存是否已完成首次有效填充。
* @param[in] 无。
* @param[out] 无。
* @retval uint8_t 返回非 0 表示缓存已可用,返回 0 表示仍未准备好。
*/
uint8_t adc_dma_ready(void);
/**
* @brief 获取 ADC DMA 原始循环缓存只读指针。
* @param[in] 无。
* @param[out] 无。
* @retval const uint16_t* 返回长度为 2 的原始缓存指针,顺序为温度通道、VREF 通道。
*/
const uint16_t *adc_dma_buffer_get(void);
/**
* @brief 执行指定通道的一次 ADC 单次采样。
* @param[in] channel 待采样的 ADC 通道号。
* @param[out] 无。
* @retval uint16_t 指定通道的原始 ADC 采样值。
* @note 在 DMA 缓存模式下,该函数优先返回已缓存的对应通道原始值;若通道不受支持则返回 0。
*/
uint16_t adc_single_read(uint8_t channel);
/**
* @brief 读取内部参考电压通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部参考电压通道的原始 ADC 值。
*/
uint16_t adc_read_vref_raw(void);
/**
* @brief 读取内部温度通道的原始 ADC 值。
* @param[in] 无。
* @param[out] 无。
* @retval uint16_t 内部温度通道的原始 ADC 值。
*/
uint16_t adc_read_temp_raw(void);
/**
* @brief 根据内部参考电压原始值计算实际 VDDA。
* @param[in] vref_raw 内部参考电压通道原始 ADC 数值。
* @param[out] 无。
* @retval float 换算得到的 VDDA 电压值,单位 V。
* @note 当输入为 0 时返回默认 3.3V,避免除零异常。
*/
float adc_calculate_vdda(uint16_t vref_raw);
/**
* @brief 根据温度原始值与 VDDA 计算芯片温度。
* @param[in] temp_raw 内部温度通道原始 ADC 数值。
* @param[in] vdda 当前供电电压,单位 V。
* @param[out] 无。
* @retval float 换算得到的芯片温度,单位 ℃。
*/
float adc_calculate_temperature(uint16_t temp_raw, float vdda);
/**
* @brief 采集一组完整 ADC 样本并完成工程值换算。
* @param[in] sample 指向输出样本结构体的指针;允许为空。
* @param[out] sample 当指针非空时,写入温度/VREF 原始值与换算结果。
* @retval 无返回值。
* @note 当 sample 为 NULL 时函数直接返回,不执行写操作。
*/
void adc_collect_sample(adc_sample_t *sample);
#endif /* DEVICE_ADC_H */
+74
View File
@@ -0,0 +1,74 @@
#ifndef DEVICE_CANFD_H
#define DEVICE_CANFD_H
#include <stdint.h>
/** @brief CANFD 状态报文的数据长度,单位字节。 */
#define CANFD_STATUS_DLEN 12U
/**
* @brief CANFD 通信模块。
* @note 负责 GPIO/模式控制、波特率配置、异步接收缓存与状态帧发送。
*/
class CanFd {
public:
/**
* @brief CANFD 运行诊断统计信息。
* @note 记录发送、接收、错误计数以及最近一次收发报文的关键信息。
*/
struct Diag {
uint32_t tx_count; /**< @brief 成功发送的报文计数。 */
uint32_t rx_count; /**< @brief 成功接收的报文计数。 */
uint32_t error_count; /**< @brief 发送失败或检测到的错误计数。 */
uint32_t last_rx_id; /**< @brief 最近一次接收报文的标识符。 */
uint32_t last_tx_id; /**< @brief 最近一次发送报文的标识符。 */
uint8_t last_payload_length; /**< @brief 最近一次收发报文的数据长度。 */
uint8_t last_rx_fd_flag; /**< @brief 最近一次接收报文的 FD 帧标志。 */
uint8_t last_rx_brs; /**< @brief 最近一次接收报文的波特率切换标志。 */
uint8_t last_rx_esi; /**< @brief 最近一次接收报文的错误状态指示位。 */
};
/**
* @brief CANFD 状态发送载荷。
* @note 当前保持协议兼容,仅包含温度与供电相关字段;VIN_ADC 单独在诊断打印中输出。
*/
struct StatusPayload {
uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */
uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */
float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */
float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */
};
static void init();
static void poll();
static void prepareStatus(const StatusPayload *status);
static void sendPrepared();
static bool hasRxFrame();
static bool consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length);
static const Diag *diag();
static void setNormalMode();
static void setSilentMode();
static void onRx0Interrupt();
private:
struct RxFrame {
uint32_t id;
uint8_t data[64];
uint8_t length;
uint8_t valid;
};
static void initGpio();
static void filterAcceptAll();
static void initNominalTiming();
static void initFdMode();
static void buildStatusFrame(const StatusPayload *status);
static Diag s_diag;
static RxFrame s_rxFrame;
};
using canfd_diag_t = CanFd::Diag;
using canfd_status_payload_t = CanFd::StatusPayload;
#endif /* DEVICE_CANFD_H */
@@ -0,0 +1,81 @@
#ifndef DEVICE_CANFD_H
#define DEVICE_CANFD_H
#include <stdint.h>
/** @brief CANFD 状态报文的数据长度,单位字节。 */
#define CANFD_STATUS_DLEN 12U
/**
* @brief CANFD 通信模块。
* @note 负责 GPIO/模式控制、波特率配置、异步接收缓存与状态帧发送。
*/
class CanFd {
public:
/**
* @brief CANFD 运行诊断统计信息。
* @note 记录发送、接收、错误计数以及最近一次收发报文的关键信息。
*/
struct Diag {
uint32_t tx_count; /**< @brief 成功发送的报文计数。 */
uint32_t rx_count; /**< @brief 成功接收的报文计数。 */
uint32_t error_count; /**< @brief 发送失败或检测到的错误计数。 */
uint32_t last_rx_id; /**< @brief 最近一次接收报文的标识符。 */
uint32_t last_tx_id; /**< @brief 最近一次发送报文的标识符。 */
uint8_t last_payload_length; /**< @brief 最近一次收发报文的数据长度。 */
uint8_t last_rx_fd_flag; /**< @brief 最近一次接收报文的 FD 帧标志。 */
uint8_t last_rx_brs; /**< @brief 最近一次接收报文的波特率切换标志。 */
uint8_t last_rx_esi; /**< @brief 最近一次接收报文的错误状态指示位。 */
};
/**
* @brief CANFD 状态发送载荷。
* @note 当前保持协议兼容,仅包含温度与供电相关字段;VIN_ADC 单独在诊断打印中输出。
*/
struct StatusPayload {
uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */
uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */
float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */
float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */
};
static void init();
static void poll();
static void prepareStatus(const StatusPayload *status);
static void sendPrepared();
static bool hasRxFrame();
static bool consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length);
static const Diag *diag();
static void setNormalMode();
static void setSilentMode();
static void onRx0Interrupt();
private:
struct RxFrame {
uint32_t id;
uint8_t data[64];
uint8_t length;
uint8_t valid;
};
struct TxBuffer {
can_trasnmit_message_struct tx_message;
uint8_t payload[64];
uint8_t valid;
};
static void initGpio();
static void filterAcceptAll();
static void initNominalTiming();
static void initFdMode();
static void buildStatusFrame(const StatusPayload *status);
static Diag s_diag;
static RxFrame s_rxFrame;
static TxBuffer s_txBuffer;
};
using canfd_diag_t = CanFd::Diag;
using canfd_status_payload_t = CanFd::StatusPayload;
#endif /* DEVICE_CANFD_H */
+99
View File
@@ -0,0 +1,99 @@
/**
* @file canfd.h
* @brief CANFD 通信接口、状态载荷与诊断信息定义。
*/
#ifndef DEVICE_CANFD_H
#define DEVICE_CANFD_H
#include <stdint.h>
/** @brief CANFD 状态报文的数据长度,单位字节。 */
#define CANFD_STATUS_DLEN 12U
/**
* @brief CANFD 运行诊断统计信息。
* @note 记录发送、接收、错误计数以及最近一次收发报文的关键信息。
*/
typedef struct {
uint32_t tx_count; /**< @brief 成功发送的报文计数。 */
uint32_t rx_count; /**< @brief 成功接收的报文计数。 */
uint32_t error_count; /**< @brief 发送失败或检测到的错误计数。 */
uint32_t last_rx_id; /**< @brief 最近一次接收报文的标识符。 */
uint32_t last_tx_id; /**< @brief 最近一次发送报文的标识符。 */
uint8_t last_payload_length; /**< @brief 最近一次收发报文的数据长度。 */
uint8_t last_rx_fd_flag; /**< @brief 最近一次接收报文的 FD 帧标志。 */
uint8_t last_rx_brs; /**< @brief 最近一次接收报文的波特率切换标志。 */
uint8_t last_rx_esi; /**< @brief 最近一次接收报文的错误状态指示位。 */
} canfd_diag_t;
/**
* @brief CANFD 状态发送载荷。
* @note 用于封装供 CAN 状态帧发送的核心状态数据。当前保持协议兼容,仅包含温度与供电相关字段;VIN_ADC 单独在诊断打印中输出。
*/
typedef struct {
uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */
uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */
float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */
float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */
} canfd_status_payload_t;
/**
* @brief 初始化 CANFD 外设、GPIO、波特率参数与接收过滤器。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
*/
void canfd_init(void);
/**
* @brief 轮询 CANFD 接收 FIFO,并缓存最近收到的一帧数据。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 若当前没有待接收报文,函数直接返回。
*/
void canfd_poll(void);
/**
* @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。
* @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。
* @param[out] 无。
* @retval 无返回值。
*/
void canfd_prepare_status(const canfd_status_payload_t *status);
/**
* @brief 发送已准备好的 CANFD 状态报文。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 若当前没有有效待发送报文,函数直接返回。
*/
void canfd_send_prepared(void);
/**
* @brief 查询是否存在尚未消费的接收报文。
* @param[in] 无。
* @param[out] 无。
* @retval uint8_t 返回非 0 表示存在待消费报文,返回 0 表示没有待消费报文。
*/
uint8_t canfd_has_rx_frame(void);
/**
* @brief 读取并消费最近一次缓存的接收报文。
* @param[out] id 输出报文标识符的指针;允许为空。
* @param[out] data 输出报文数据缓存区指针;允许为空。
* @param[out] length 输出报文数据长度的指针;允许为空。
* @retval uint8_t 返回 1 表示成功消费一帧报文,返回 0 表示当前无有效缓存报文。
*/
uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length);
/**
* @brief 获取 CANFD 诊断统计信息只读指针。
* @param[in] 无。
* @param[out] 无。
* @retval const canfd_diag_t* 指向内部诊断统计结构体的只读指针。
*/
const canfd_diag_t *canfd_get_diag(void);
#endif /* DEVICE_CANFD_H */
+99
View File
@@ -0,0 +1,99 @@
/**
* @file canfd.h
* @brief CANFD 通信接口、状态载荷与诊断信息定义。
*/
#ifndef DEVICE_CANFD_H
#define DEVICE_CANFD_H
#include <stdint.h>
/** @brief CANFD 状态报文的数据长度,单位字节。 */
#define CANFD_STATUS_DLEN 12U
/**
* @brief CANFD 运行诊断统计信息。
* @note 记录发送、接收、错误计数以及最近一次收发报文的关键信息。
*/
typedef struct {
uint32_t tx_count; /**< @brief 成功发送的报文计数。 */
uint32_t rx_count; /**< @brief 成功接收的报文计数。 */
uint32_t error_count; /**< @brief 发送失败或检测到的错误计数。 */
uint32_t last_rx_id; /**< @brief 最近一次接收报文的标识符。 */
uint32_t last_tx_id; /**< @brief 最近一次发送报文的标识符。 */
uint8_t last_payload_length; /**< @brief 最近一次收发报文的数据长度。 */
uint8_t last_rx_fd_flag; /**< @brief 最近一次接收报文的 FD 帧标志。 */
uint8_t last_rx_brs; /**< @brief 最近一次接收报文的波特率切换标志。 */
uint8_t last_rx_esi; /**< @brief 最近一次接收报文的错误状态指示位。 */
} canfd_diag_t;
/**
* @brief CANFD 状态发送载荷。
* @note 用于封装温度与供电电压相关的状态数据,供状态帧打包发送。
*/
typedef struct {
uint16_t temp_raw; /**< @brief 内部温度通道原始 ADC 数值。 */
uint16_t vref_raw; /**< @brief 内部参考电压通道原始 ADC 数值。 */
float vdda; /**< @brief 根据内部参考电压换算得到的 MCU 供电电压,单位 V。 */
float temperature; /**< @brief 根据温度通道换算得到的芯片温度,单位 ℃。 */
} canfd_status_payload_t;
/**
* @brief 初始化 CANFD 外设、GPIO、波特率参数与接收过滤器。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
*/
void canfd_init(void);
/**
* @brief 轮询 CANFD 接收 FIFO,并缓存最近收到的一帧数据。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 若当前没有待接收报文,函数直接返回。
*/
void canfd_poll(void);
/**
* @brief 根据状态载荷准备一帧待发送的 CANFD 状态报文。
* @param[in] status 指向状态载荷的指针;为空时将清除待发送标志。
* @param[out] 无。
* @retval 无返回值。
*/
void canfd_prepare_status(const canfd_status_payload_t *status);
/**
* @brief 发送已准备好的 CANFD 状态报文。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 若当前没有有效待发送报文,函数直接返回。
*/
void canfd_send_prepared(void);
/**
* @brief 查询是否存在尚未消费的接收报文。
* @param[in] 无。
* @param[out] 无。
* @retval uint8_t 返回非 0 表示存在待消费报文,返回 0 表示没有待消费报文。
*/
uint8_t canfd_has_rx_frame(void);
/**
* @brief 读取并消费最近一次缓存的接收报文。
* @param[out] id 输出报文标识符的指针;允许为空。
* @param[out] data 输出报文数据缓存区指针;允许为空。
* @param[out] length 输出报文数据长度的指针;允许为空。
* @retval uint8_t 返回 1 表示成功消费一帧报文,返回 0 表示当前无有效缓存报文。
*/
uint8_t canfd_consume_rx_frame(uint32_t *id, uint8_t *data, uint8_t *length);
/**
* @brief 获取 CANFD 诊断统计信息只读指针。
* @param[in] 无。
* @param[out] 无。
* @retval const canfd_diag_t* 指向内部诊断统计结构体的只读指针。
*/
const canfd_diag_t *canfd_get_diag(void);
#endif /* DEVICE_CANFD_H */
+17
View File
@@ -0,0 +1,17 @@
/**
* @file temp_sensor.cpp
* @brief 温度传感模块实现。
*/
#include "device/adc.h"
#include "device/temp_sensor.h"
void TempSensor::init()
{
Adc::init();
}
void TempSensor::sample(Data *data)
{
Adc::collectSample(data);
}
@@ -0,0 +1,25 @@
/**
* @file temp_sensor.cpp
* @brief 温度传感采样实现。
*/
#include "device/adc.h"
#include "device/temp_sensor.h"
/**
* @brief 初始化温度传感采样所需硬件资源。
* @note 当前通过初始化 ADC 模块完成内部温度通道准备。
*/
void temp_sensor_init(void)
{
adc_hw_init();
}
/**
* @brief 采集一次温度传感样本并输出结果。
* @param[out] data 指向输出样本结构体的指针;其语义与 adc_collect_sample() 一致。
*/
void temp_sensor_sample(temp_sensor_data_t *data)
{
adc_collect_sample(data);
}
+20
View File
@@ -0,0 +1,20 @@
#ifndef DEVICE_TEMP_SENSOR_H
#define DEVICE_TEMP_SENSOR_H
#include "device/adc.h"
/**
* @brief 温度采样模块。
* @note 当前作为 ADC 模块的轻量门面,复用 Adc::Sample 作为结果类型。
*/
class TempSensor {
public:
using Data = Adc::Sample;
static void init();
static void sample(Data *data);
};
using temp_sensor_data_t = TempSensor::Data;
#endif /* DEVICE_TEMP_SENSOR_H */
@@ -0,0 +1,33 @@
/**
* @file temp_sensor.h
* @brief 温度传感采样接口定义。
*/
#ifndef DEVICE_TEMP_SENSOR_H
#define DEVICE_TEMP_SENSOR_H
#include <stdint.h>
#include "device/adc.h"
/**
* @brief 温度传感采样数据类型别名。
* @note 复用 ADC 采样结果结构,统一保存温度与参考电压原始值及换算值。
*/
typedef adc_sample_t temp_sensor_data_t;
/**
* @brief 初始化温度传感采样所需硬件资源。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 当前通过初始化 ADC 模块完成内部温度通道准备。
*/
void temp_sensor_init(void);
/**
* @brief 采集一次温度传感样本并输出结果。
* @param[out] data 指向输出样本结构体的指针;其语义与 adc_collect_sample() 一致。
* @retval 无返回值。
*/
void temp_sensor_sample(temp_sensor_data_t *data);
#endif /* DEVICE_TEMP_SENSOR_H */
+77
View File
@@ -0,0 +1,77 @@
/**
* @file usart0.cpp
* @brief USART0 串口模块实现与 printf 重定向。
*/
#include "main.h"
#include "device/usart0.h"
#include "sys/config.h"
uint8_t Usart0::s_txBuf[Usart0::kTxBufSize];
volatile uint16_t Usart0::s_txHead = 0U;
volatile uint16_t Usart0::s_txTail = 0U;
uint16_t Usart0::nextIndex(uint16_t idx)
{
return (idx + 1U) % kTxBufSize;
}
bool Usart0::isBufferFull()
{
return nextIndex(s_txHead) == s_txTail;
}
bool Usart0::isBufferEmpty()
{
return s_txHead == s_txTail;
}
int Usart0::putChar(int ch, FILE *f)
{
(void)f;
uint16_t next = nextIndex(s_txHead);
while(next == s_txTail) { /* 缓冲满,忙等重试 */ }
s_txBuf[s_txHead] = static_cast<uint8_t>(ch);
s_txHead = next;
usart_interrupt_enable(USART0, USART_INT_TBE);
return ch;
}
void Usart0::onInterrupt()
{
if(usart_interrupt_flag_get(USART0, USART_INT_FLAG_TBE) != RESET) {
if(!isBufferEmpty()) {
usart_data_transmit(USART0, s_txBuf[s_txTail]);
s_txTail = nextIndex(s_txTail);
}
if(isBufferEmpty()) {
usart_interrupt_disable(USART0, USART_INT_TBE);
}
}
}
void Usart0::configurePins()
{
rcu_periph_clock_enable(RCU_GPIOA);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
}
void Usart0::init()
{
rcu_periph_clock_enable(RCU_USART0);
usart_deinit(USART0);
usart_baudrate_set(USART0, CONFIG_USART0_BAUDRATE);
usart_word_length_set(USART0, USART_WL_8BIT);
usart_stop_bit_set(USART0, USART_STB_1BIT);
usart_parity_config(USART0, USART_PM_NONE);
usart_receive_config(USART0, USART_RECEIVE_ENABLE);
usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
usart_enable(USART0);
nvic_irq_enable(USART0_IRQn, 2U, 0U);
}
/* ----- 中断入口 / printf 重定向的 C 包装 --------------------------- */
extern "C" int fputc(int ch, FILE *f) { return Usart0::putChar(ch, f); }
extern "C" void USART0_IRQHandler(void) { Usart0::onInterrupt(); }
+109
View File
@@ -0,0 +1,109 @@
/**
* @file usart0.cpp
* @brief USART0 串口配置与 printf 重定向实现。
* @note 使用 TX 中断 + 环形缓冲替代逐字节忙等 TBE,降低 CPU 占用。
*/
#include "main.h"
#include "device/usart0.h"
#include "sys/config.h"
#define USARTx USART0
#define USART_TX_BUF_SIZE 256U
namespace {
uint8_t g_tx_buf[USART_TX_BUF_SIZE];
volatile uint16_t g_tx_head = 0U;
volatile uint16_t g_tx_tail = 0U;
inline uint16_t tx_buf_next(uint16_t idx)
{
return (idx + 1U) % USART_TX_BUF_SIZE;
}
inline bool tx_buf_full(void)
{
return tx_buf_next(g_tx_head) == g_tx_tail;
}
inline bool tx_buf_empty(void)
{
return g_tx_head == g_tx_tail;
}
} // namespace
/**
* @brief 重定向标准输出字符到 USART0。
* @param[in] ch 待输出的字符。
* @param[in] f 标准流指针;当前实现中未实际使用。
* @retval 返回已发送的字符值。
* @note 将字符写入环形发送缓冲,由 USART0 TXE 中断负责实际发送。
*/
extern "C" int fputc(int ch, FILE *f)
{
(void)f;
uint16_t next = tx_buf_next(g_tx_head);
while(next == g_tx_tail) {
/* 缓冲满,忙等一小段时间后重试;
* 在 115200 波特率下通常不会发生 */
}
g_tx_buf[g_tx_head] = static_cast<uint8_t>(ch);
g_tx_head = next;
/* 启动 TXE 中断以触发实际硬件发送 */
usart_interrupt_enable(USARTx, USART_INT_TBE);
return ch;
}
/**
* @brief USART0 中断服务例程。
* @note 处理 TXE 发送缓冲区空中断,从环形缓冲取下一字节发送。
*/
extern "C" void USART0_IRQHandler(void)
{
if(usart_interrupt_flag_get(USARTx, USART_INT_FLAG_TBE) != RESET) {
if(!tx_buf_empty()) {
usart_data_transmit(USARTx, g_tx_buf[g_tx_tail]);
g_tx_tail = tx_buf_next(g_tx_tail);
}
/* 缓冲已空,关 TXE 中断,等下次 fputc 再开 */
if(tx_buf_empty()) {
usart_interrupt_disable(USARTx, USART_INT_TBE);
}
}
}
/**
* @brief 配置 USART0 相关 GPIO 引脚。
*/
void usart0_gpio_config(void)
{
rcu_periph_clock_enable(RCU_GPIOA);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
}
/**
* @brief 配置并使能 USART0 外设。
* @note 当前使用 8N1 格式,115200 波特率,收发同时使能。
* 同步配置 NVIC 以便 TXE 中断驱动发送。
*/
void usart0_config(void)
{
rcu_periph_clock_enable(RCU_USART0);
usart_deinit(USARTx);
usart_baudrate_set(USARTx, CONFIG_USART0_BAUDRATE);
usart_word_length_set(USARTx, USART_WL_8BIT);
usart_stop_bit_set(USARTx, USART_STB_1BIT);
usart_parity_config(USARTx, USART_PM_NONE);
usart_receive_config(USARTx, USART_RECEIVE_ENABLE);
usart_transmit_config(USARTx, USART_TRANSMIT_ENABLE);
usart_enable(USARTx);
/* 使能 USART0 NVIC,用于 TXE 中断驱动发送 */
nvic_irq_enable(USART0_IRQn, 2U, 0U);
}
@@ -0,0 +1,54 @@
/**
* @file usart0.cpp
* @brief USART0 串口配置与 printf 重定向实现。
*/
#include "main.h"
#include "device/usart0.h"
#include "sys/config.h"
#define USARTx USART0
/**
* @brief 重定向标准输出字符到 USART0。
* @param[in] ch 待输出的字符。
* @param[in] f 标准流指针;当前实现中未实际使用。
* @retval 返回已发送的字符值。
*/
extern "C" int fputc(int ch, FILE *f)
{
(void)f;
while(RESET == usart_flag_get(USARTx, USART_FLAG_TBE)) {
}
usart_data_transmit(USARTx, (uint16_t)ch);
return ch;
}
/**
* @brief 配置 USART0 相关 GPIO 引脚。
*/
void usart0_gpio_config(void)
{
rcu_periph_clock_enable(RCU_GPIOA);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
}
/**
* @brief 配置并使能 USART0 外设。
* @note 当前使用 8N1 格式,115200 波特率,收发同时使能。
*/
void usart0_config(void)
{
rcu_periph_clock_enable(RCU_USART0);
usart_deinit(USARTx);
usart_baudrate_set(USARTx, CONFIG_USART0_BAUDRATE);
usart_word_length_set(USARTx, USART_WL_8BIT);
usart_stop_bit_set(USARTx, USART_STB_1BIT);
usart_parity_config(USARTx, USART_PM_NONE);
usart_receive_config(USARTx, USART_RECEIVE_ENABLE);
usart_transmit_config(USARTx, USART_TRANSMIT_ENABLE);
usart_enable(USARTx);
}
+29
View File
@@ -0,0 +1,29 @@
#ifndef DEVICE_USART0_H
#define DEVICE_USART0_H
#include <stdint.h>
#include <stdio.h>
/**
* @brief USART0 串口模块。
* @note 负责 GPIO 初始化、串口配置、TX 环形缓冲与中断驱动发送。
*/
class Usart0 {
public:
static void configurePins();
static void init();
static int putChar(int ch, FILE *f);
static void onInterrupt();
private:
static uint16_t nextIndex(uint16_t idx);
static bool isBufferFull();
static bool isBufferEmpty();
static constexpr uint16_t kTxBufSize = 256U;
static uint8_t s_txBuf[kTxBufSize];
static volatile uint16_t s_txHead;
static volatile uint16_t s_txTail;
};
#endif /* DEVICE_USART0_H */
+25
View File
@@ -0,0 +1,25 @@
/**
* @file usart0.h
* @brief USART0 串口打印与基础配置接口定义。
*/
#ifndef DEVICE_USART0_H
#define DEVICE_USART0_H
/**
* @brief 配置 USART0 相关 GPIO 引脚。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
*/
void usart0_gpio_config(void);
/**
* @brief 配置并使能 USART0 外设。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note 当前串口参数由实现文件中的默认宏定义决定。
*/
void usart0_config(void);
#endif /* DEVICE_USART0_H */