FilmForge: RS485 data link (USART0+DE PA8) and NTC temp (PC5 ADC0)

This commit is contained in:
2026-10-09 19:13:15 +08:00
parent 8feba5e015
commit 24ddb7046e
12 changed files with 402 additions and 197 deletions
+4 -7
View File
@@ -36,6 +36,7 @@ OF SUCH DAMAGE.
#include "systick.h"
#include "uart.h"
#include "uart_ring_buffer.h"
#include "rs485.h"
#include "led.h"
#include "board_config.h"
@@ -141,16 +142,12 @@ void SysTick_Handler(void) {
delay_decrement();
}
/* 调试串口静态绑定 USART1(PA2/PA3),直接调用其处理函数。
若业务改用其它串口,同步修改 board_config.h 的 UART_* 并在此补对应 IRQHandler。 */
/* 串口中断静态绑定:USART0 = RS485 数据口(rs485.c),USART1 = debug 串口(uart.c)。
若业务改用其它串口,同步修改 board_config.h 并在此调整对应 IRQHandler。 */
void USART0_IRQHandler(void) {
usart0_irq_handler();
rs485_irq_handler();
}
void USART1_IRQHandler(void) {
usart1_irq_handler();
}
void USART2_IRQHandler(void) {
usart2_irq_handler();
}
+25 -44
View File
@@ -1,51 +1,21 @@
/*!
\file main.c
\brief GD32F4xx 工程模板入口(LED 心跳 + 串口收发框架)
\brief FilmForge 热压模具温控固件入口
基于 GD32E23x 工程模板移植,目标芯片 GD32F405RET6(Cortex-M4 @168MHz,512KB Flash)。
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
自制热压模具(塑型 PC 膜):加热驱动 + 测温(NTC)+ RS485 数据口。
基于 GD32F4xx 工程模板,目标芯片 GD32F405RET6(Cortex-M4 @168MHz)。
*/
#include "gd32f4xx.h"
#include "systick.h"
#include "uart.h"
#include "uart_ring_buffer.h"
#include "rs485.h"
#include "ntc.h"
#include "led.h"
#include <stdio.h>
#include "board_config.h"
/* TODO: 以下模块尚未从 GD32E23x 工程移植,编译验证通过后逐步补充:
* - i2c.c/i2c.h I2C 主机读写驱动(硬件 I2C0 + 总线恢复)
* - command.c/h 串口命令协议解析(D5 帧格式)
* 移植时将对应文件加入 CMakeLists.txt 的 TARGET_SRC 即可。
*/
/*!
\brief main function
\param[in] none
@@ -61,22 +31,33 @@ int main(void)
uart_ring_buffer_init();
uart_init();
rs485_init();
ntc_init();
#if DEBUG_MODE == CFG_ENABLE
printf("Hello World!\r\n");
printf("GD32F405 template, flash size: %u KB\r\n", GD32_FLASH_SIZE_KB);
printf("FilmForge FW %s, built %s\r\n", PROJECT_VERSION, BUILD_DATE);
printf("RS485 data link ready, NTC ready\r\n");
#endif
#if SEGGER_RTT_DETECTION == CFG_ENABLE
RTT_printf(0, "Hello World!\r\n");
RTT_printf(0, "FilmForge FW\r\n");
#endif
printf("Hello World!\r\n");
/* ========== Command Testing ========== */
/* ========== */
while(1){
/* TODO: command_process(); 命令模块移植后启用 */
/* RS485 数据口处理(拉通阶段:回显) */
rs485_process();
/* debug 串口周期打印 NTC 温度(约 1s 一次) */
#if DEBUG_MODE == CFG_ENABLE
{
static uint16_t temp_report_counter = 0;
if (++temp_report_counter >= 100U) { /* 主循环 10ms 一圈 → 1s */
temp_report_counter = 0;
printf("NTC temp: %.1f C\r\n", ntc_read_temperature());
}
}
#endif
delay_ms(10);
}
}
+70
View File
@@ -0,0 +1,70 @@
#include "ntc.h"
#include "gd32f4xx_adc.h"
#include "gd32f4xx_rcu.h"
#include "gd32f4xx_gpio.h"
#include "board_config.h"
#include <math.h>
/**
* @brief 配置 NTC 引脚(PC5 模拟输入)与 ADC0 常规通道。
*/
static void ntc_gpio_adc_config(void) {
/* 使能 GPIOC 与 ADC0 时钟 */
rcu_periph_clock_enable(NTC_GPIO_RCU);
rcu_periph_clock_enable(NTC_ADC_RCU);
/* PC5 模拟输入模式 */
gpio_mode_set(NTC_GPIO_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, NTC_GPIO_PIN);
/* ADC0 独立模式,常规通道软件触发、单次转换、右对齐 */
adc_sync_mode_config(ADC_SYNC_MODE_INDEPENDENT);
adc_special_function_config(NTC_ADC, ADC_CONTINUOUS_MODE, DISABLE);
adc_special_function_config(NTC_ADC, ADC_INSERTED_CHANNEL, DISABLE);
adc_external_trigger_config(NTC_ADC, ADC_ROUTINE_CHANNEL, EXTERNAL_TRIGGER_DISABLE);
adc_data_alignment_config(NTC_ADC, ADC_DATAALIGN_RIGHT);
adc_channel_length_config(NTC_ADC, ADC_ROUTINE_CHANNEL, 1U);
adc_routine_channel_config(NTC_ADC, 0U, NTC_ADC_CHANNEL, NTC_SAMPLETIME);
adc_enable(NTC_ADC);
adc_calibration_enable(NTC_ADC);
}
void ntc_init(void) {
ntc_gpio_adc_config();
}
/**
* @brief 触发一次转换并读取 12bit 原始值。
*/
static uint16_t ntc_read_raw_once(void) {
adc_flag_clear(NTC_ADC, ADC_FLAG_EOC);
adc_software_trigger_enable(NTC_ADC, ADC_ROUTINE_CHANNEL);
/* 等待转换完成(EOC) */
while (SET != adc_flag_get(NTC_ADC, ADC_FLAG_EOC)) {}
return (uint16_t)adc_routine_data_read(NTC_ADC);
}
float ntc_read_temperature(void) {
uint32_t sum = 0U;
/* 多次采样均值降噪 */
for (uint8_t i = 0; i < NTC_SAMPLE_COUNT; i++) {
sum += ntc_read_raw_once();
}
float adc_val = (float)sum / (float)NTC_SAMPLE_COUNT;
/* 分压点电压:上拉 R_PULL 在 VREF 侧,NTC 接 GND 侧 */
float voltage = adc_val * NTC_VREF / NTC_ADC_MAX;
/* 反解 NTC 阻值:V = VREF * R / (R_PULL + R) => R = R_PULL * V / (VREF - V) */
if (voltage <= 0.0f || voltage >= NTC_VREF) {
/* 开路/短路等异常,返回 -273.15 表示无效 */
return -273.15f;
}
float r_ntc = NTC_R_PULL * voltage / (NTC_VREF - voltage);
/* B 值公式:1/T = 1/T25 + ln(R/R25)/B */
float t_k = 1.0f / (1.0f / NTC_T25 + logf(r_ntc / NTC_R25) / (float)NTC_B_VALUE);
return t_k - 273.15f;
}
+126
View File
@@ -0,0 +1,126 @@
#include "rs485.h"
#include "gd32f4xx_usart.h"
#include "gd32f4xx_rcu.h"
#include "gd32f4xx_gpio.h"
#include "board_config.h"
#include "uart.h"
/* 独立接收环形缓冲区(与 debug 串口的 uart_ring_buffer 隔离),
采用"预留一格"区分空/满,最大可用容量为 RS485_RX_BUFFER_SIZE-1 */
static __IO uint8_t rs485_rx_buffer[RS485_RX_BUFFER_SIZE];
static __IO uint8_t rs485_write_index = 0;
static __IO uint8_t rs485_read_index = 0;
static inline uint32_t rs485_irq_save(void) {
uint32_t primask = __get_PRIMASK();
__disable_irq();
return primask;
}
static inline void rs485_irq_restore(uint32_t primask) {
if ((primask & 0x1) == 0) {
__enable_irq();
}
}
/**
* @brief 配置 RS485 收发引脚(PA9 TX / PA10 RX 复用,DE PA8 推挽输出)。
*/
static void rs485_gpio_config(void) {
/* 使能 GPIOA 和 USART0 时钟 */
rcu_periph_clock_enable(RS485_GPIO_RCU);
rcu_periph_clock_enable(RS485_RCU);
/* PA9 为 USART0_TX,PA10 为 USART0_RX(AF7) */
gpio_af_set(RS485_GPIO_PORT, GPIO_AF_7, RS485_TX_PIN | RS485_RX_PIN);
gpio_mode_set(RS485_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, RS485_TX_PIN | RS485_RX_PIN);
gpio_output_options_set(RS485_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, RS485_TX_PIN | RS485_RX_PIN);
/* DE 引脚为普通推挽输出,默认接收态 */
rcu_periph_clock_enable(RS485_DE_RCU);
gpio_mode_set(RS485_DE_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, RS485_DE_PIN);
gpio_output_options_set(RS485_DE_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, RS485_DE_PIN);
gpio_bit_reset(RS485_DE_PORT, RS485_DE_PIN);
}
void rs485_init(void) {
rs485_gpio_config();
/* 配置波特率、数据位、停止位等 */
usart_deinit(RS485_PHY);
usart_word_length_set(RS485_PHY, USART_WL_8BIT);
usart_stop_bit_set(RS485_PHY, USART_STB_1BIT);
usart_parity_config(RS485_PHY, USART_PM_NONE);
usart_baudrate_set(RS485_PHY, RS485_BAUDRATE);
usart_receive_config(RS485_PHY, USART_RECEIVE_ENABLE);
usart_transmit_config(RS485_PHY, USART_TRANSMIT_ENABLE);
usart_enable(RS485_PHY);
/* 使能接收非空中断 */
nvic_irq_enable(RS485_IRQ, 0, 0);
usart_interrupt_enable(RS485_PHY, USART_INT_RBNE);
}
void rs485_send(const uint8_t *data, uint16_t len) {
/* 进入发送态 */
gpio_bit_set(RS485_DE_PORT, RS485_DE_PIN);
for (uint16_t i = 0; i < len; i++) {
/* 等待发送缓冲区空 */
while (RESET == usart_flag_get(RS485_PHY, USART_FLAG_TBE)) {}
usart_data_transmit(RS485_PHY, data[i]);
}
/* 等待最后一字节移位完成,再切回接收态(避免截断尾字节) */
while (RESET == usart_flag_get(RS485_PHY, USART_FLAG_TC)) {}
/* 回到接收态 */
gpio_bit_reset(RS485_DE_PORT, RS485_DE_PIN);
}
uint8_t rs485_rx_available(void) {
uint8_t w = rs485_write_index;
uint8_t r = rs485_read_index;
return (uint8_t)((w + RS485_RX_BUFFER_SIZE - r) % RS485_RX_BUFFER_SIZE);
}
int rs485_rx_get(void) {
if (rs485_read_index == rs485_write_index) return -1; /* 空 */
uint8_t data = rs485_rx_buffer[rs485_read_index];
rs485_read_index = (rs485_read_index + 1) % RS485_RX_BUFFER_SIZE;
return data;
}
void rs485_process(void) {
while (rs485_rx_available() > 0) {
int b = rs485_rx_get();
if (b < 0) {
break;
}
/* 拉通阶段:原样回发 RS485,同时转发 debug 串口便于观察 */
rs485_send((const uint8_t *)&b, 1);
debug_uart_write((uint8_t)b);
}
}
void rs485_irq_handler(void) {
/* 处理USART0的接收中断 */
if (usart_interrupt_flag_get(RS485_PHY, USART_INT_FLAG_RBNE)) {
uint8_t data = usart_data_receive(RS485_PHY);
uint32_t primask = rs485_irq_save();
uint8_t next = (rs485_write_index + 1) % RS485_RX_BUFFER_SIZE;
if (next != rs485_read_index) {
/* 缓冲区未满 */
rs485_rx_buffer[rs485_write_index] = data;
rs485_write_index = next;
}
/* 缓冲满时丢弃 */
rs485_irq_restore(primask);
}
/* 处理USART0的空闲中断(帧间隔提示,后续协议解析用) */
if (usart_interrupt_flag_get(RS485_PHY, USART_INT_FLAG_IDLE)) {
usart_interrupt_flag_clear(RS485_PHY, USART_INT_FLAG_IDLE);
}
}
+6 -46
View File
@@ -10,20 +10,12 @@ void uart_init(void) {
rcu_periph_clock_enable(UART_GPIO_RCU);
rcu_periph_clock_enable(UART_RCU);
/* 配置 PA9 为 USART0_TX,PA10 为 USART0_RX(AF7) */
/* 配置 PA2 为 USART1_TX,PA3 为 USART1_RX(AF7),debug 口纯串口 */
gpio_af_set(UART_GPIO_PORT, GPIO_AF_7, UART_TX_PIN | UART_RX_PIN);
gpio_mode_set(UART_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, UART_TX_PIN | UART_RX_PIN);
gpio_output_options_set(UART_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, UART_TX_PIN | UART_RX_PIN);
#if RS485_MODE == CFG_ENABLE
/* 配置 RS485 DE 引脚为普通推挽输出(默认接收态) */
rcu_periph_clock_enable(RS485_DE_RCU);
gpio_mode_set(RS485_DE_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, RS485_DE_PIN);
gpio_output_options_set(RS485_DE_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, RS485_DE_PIN);
gpio_bit_reset(RS485_DE_PORT, RS485_DE_PIN);
#endif
/* 配置波特率、数据位、停止位等 */
usart_deinit(UART_PHY);
usart_word_length_set(UART_PHY, USART_WL_8BIT);
@@ -47,40 +39,21 @@ void uart_rx_irq_resume(void) {
usart_interrupt_enable(UART_PHY, USART_INT_RBNE);
}
#if RS485_MODE == CFG_ENABLE
void rs485_de_set(void) {
gpio_bit_set(RS485_DE_PORT, RS485_DE_PIN); /* 发送态 */
void debug_uart_write(uint8_t byte) {
/* 等待发送缓冲区空后写入 debug 串口 */
while (RESET == usart_flag_get(DEBUG_UART, USART_FLAG_TBE)) {}
usart_data_transmit(DEBUG_UART, byte);
}
void rs485_de_clear(void) {
gpio_bit_reset(RS485_DE_PORT, RS485_DE_PIN); /* 接收态 */
}
#endif
/******************************************************************************/
/* 具体的中断处理函数实现 */
/******************************************************************************/
void usart0_irq_handler(void) {
// 处理USART0的接收中断
if(usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)) {
uint8_t data = usart_data_receive(USART0);
// 使用原有的环形缓冲区处理逻辑
(void)uart_ring_buffer_put(data); // 缓冲满时丢弃,返回值可用于统计
}
// 处理USART0的空闲中断
if(usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE)) {
usart_interrupt_flag_clear(USART0, USART_INT_FLAG_IDLE);
// 在这里添加空闲中断处理逻辑
}
}
void usart1_irq_handler(void) {
// 处理USART1的接收中断
if(usart_interrupt_flag_get(USART1, USART_INT_FLAG_RBNE)) {
uint8_t data = usart_data_receive(USART1);
(void)uart_ring_buffer_put(data);
(void)uart_ring_buffer_put(data); // 缓冲满时丢弃,返回值可用于统计
}
// 处理USART1的空闲中断
@@ -89,19 +62,6 @@ void usart1_irq_handler(void) {
}
}
void usart2_irq_handler(void) {
// 处理USART2的接收中断
if(usart_interrupt_flag_get(USART2, USART_INT_FLAG_RBNE)) {
uint8_t data = usart_data_receive(USART2);
(void)uart_ring_buffer_put(data);
}
// 处理USART2的空闲中断
if(usart_interrupt_flag_get(USART2, USART_INT_FLAG_IDLE)) {
usart_interrupt_flag_clear(USART2, USART_INT_FLAG_IDLE);
}
}
/* 调试串口初始化(与 uart_init 相同配置的独立入口,便于单独初始化打印口) */
void debug_usart_init(void)
{