调试key LCD rs485都正常运行,修复了bug,但是DMA接收的问题没有解决

This commit is contained in:
2026-01-26 15:48:15 +08:00
parent 2b93c00a16
commit f9f0016f69
14 changed files with 1600 additions and 3523 deletions

View File

@@ -10,6 +10,7 @@
* 显示屏: 160x160像素 * 显示屏: 160x160像素
* 段地址: SEG112~SEG271 * 段地址: SEG112~SEG271
* 外部MPU晶振: 20MHz * 外部MPU晶振: 20MHz
* 切记不要在中断中操作屏幕,不然会打断屏幕的时许造成不可预测的问题!!!!!!!!!!!!!!!
******************************************************************************/ ******************************************************************************/
#include "160160D.h" #include "160160D.h"
@@ -1144,10 +1145,14 @@ void Fault_Disp(void)
/** /**
* @brief 显示按键运行状态指示 * @brief 显示按键运行状态指示屏幕的左上方显示3个点
* @param Flag 状态标志非0=显示运行指示0=清除指示 * @param Flag 状态标志非0=显示运行指示0=清除指示
* @note 在屏幕左上角坐标0,0显示按键运行状态 * @note 在屏幕左上角坐标0,0显示按键运行状态
* 显示3个像素点表示运行状态 * 显示3个像素点表示运行状态
* *
* *
*
* *
* @retval 无 * @retval 无
*/ */
void KeyRun_Disp(uint32_t Flag) void KeyRun_Disp(uint32_t Flag)
@@ -1171,9 +1176,16 @@ void KeyRun_Disp(uint32_t Flag)
/** /**
* @brief 全屏显示图像数据 * @brief 全屏显示图像数据
* @param ptr 图像数据指针160x160像素每像素2位共3200字节 * @param ptr 图像数据指针160x160像素每像素2位共3200字节
* @note 将图像数据按4K色模式2位/像素)写入整个屏幕 * @note 将图像数据按4K色模式 写入整个屏幕
* 图像数据格式:每字节包含4个像素点bit7-6, bit5-4, bit3-2, bit1-0 * 图像数据格式:每字节包含8个像素点
* 每行160像素 = 40字节共160行 * 写入数据:| D7 D6 D5 D4 | D3 D2 D1 D0 |
* └ 第一个像素 ┘ └ 第二个像素 ┘
* 写入数据:| D7 D6 D5 D4 | D3 D2 D1 D0 |
* └ 第三个像素 ┘ └ 第四个像素 ┘
* 写入数据:| D7 D6 D5 D4 | D3 D2 D1 D0 |
* └ 第五个像素 ┘ └ 第六个像素 ┘
* 写入数据:| D7 D6 D5 D4 | D3 D2 D1 D0 |
* └ 第七个像素 ┘ └ 第八个像素 ┘
* @retval 无 * @retval 无
*/ */
void ScreenPrintf(uint8_t* ptr) void ScreenPrintf(uint8_t* ptr)
@@ -1184,17 +1196,17 @@ void ScreenPrintf(uint8_t* ptr)
SetAddress(0, 0); /* 设置起始地址为屏幕左上角 */ SetAddress(0, 0); /* 设置起始地址为屏幕左上角 */
/* 循环显示160行 */ /* 循环显示 160 行 */
for(i = 0; i < 160; i++) for(i = 0; i < 160; i++)
{ {
/* 每行20个字节每字节4个像素点,共80个像素点但实际显示需要更多 */ /* 每行20个字节每字节 8 个像素点,共 160 个像素点 */
for(j = 0; j < 20; j++) for(j = 0; j < 20; j++)
{ {
/* 将每字节的4个2位像素值转换为显示数据 */ /* 将每字节的 4 个 2 位像素值转换为显示数据 */
WriteData(disp_point[(ptr[i*20 + j] >> 6) & 0x03]); /* bit7-6 */ WriteData(disp_point[(ptr[i*20 + j] >> 6) & 0x03]); /* bit0-1 */
WriteData(disp_point[(ptr[i*20 + j] >> 4) & 0x03]); /* bit5-4 */ WriteData(disp_point[(ptr[i*20 + j] >> 4) & 0x03]); /* bit2-3 */
WriteData(disp_point[(ptr[i*20 + j] >> 2) & 0x03]); /* bit3-2 */ WriteData(disp_point[(ptr[i*20 + j] >> 2) & 0x03]); /* bit4-5 */
WriteData(disp_point[(ptr[i*20 + j] >> 0) & 0x03]); /* bit1-0 */ WriteData(disp_point[(ptr[i*20 + j] >> 0) & 0x03]); /* bit6-7 */
} }
WriteData(0x00); /* 补全每行末尾的数据使总点数能被3整除 */ WriteData(0x00); /* 补全每行末尾的数据使总点数能被3整除 */
} }

View File

@@ -14,7 +14,6 @@
******************************************************************************/ ******************************************************************************/
#include "key.h" #include "key.h"
#include "160160D.h"
/* ============================================================================ /* ============================================================================
* 按键配置结构体定义 * 按键配置结构体定义
@@ -43,8 +42,7 @@ typedef struct {
* | KEY_LEFT | GPIOD | PD13 | KEY_LEFT | 左键 | * | KEY_LEFT | GPIOD | PD13 | KEY_LEFT | 左键 |
* | KEY_RIGHT | GPIOD | PD8 | KEY_RIGHT | 右键 | * | KEY_RIGHT | GPIOD | PD8 | KEY_RIGHT | 右键 |
* | KEY_ESC | GPIOB | PB15 | KEY_ESC | 取消键 | * | KEY_ESC | GPIOB | PB15 | KEY_ESC | 取消键 |
* | KEY_ADD | GPIOB | PB3 | KEY_ADD | 加键(需禁用 JTAG | * | KEY_RESET | GPIOB | PB3 | KEY_ADD | 加键(需禁用 JTAG |
* | KEY_RESET | GPIOD | PD15 | KEY_RESET | 复位键 |
* @note PB3 引脚默认被 JTAG 占用,需要禁用 JTAG 才能作为普通 GPIO 使用 * @note PB3 引脚默认被 JTAG 占用,需要禁用 JTAG 才能作为普通 GPIO 使用
*/ */
static const KeyConfig_t key_configs[] = { static const KeyConfig_t key_configs[] = {
@@ -54,8 +52,7 @@ static const KeyConfig_t key_configs[] = {
{GPIO_PIN_13, GPIOD, KEY_LEFT}, /**< 左键PD13 */ {GPIO_PIN_13, GPIOD, KEY_LEFT}, /**< 左键PD13 */
{GPIO_PIN_8, GPIOD, KEY_RIGHT}, /**< 右键PD8 */ {GPIO_PIN_8, GPIOD, KEY_RIGHT}, /**< 右键PD8 */
{GPIO_PIN_15, GPIOB, KEY_ESC}, /**< 取消键PB15 */ {GPIO_PIN_15, GPIOB, KEY_ESC}, /**< 取消键PB15 */
{GPIO_PIN_3, GPIOB, KEY_ADD}, /**< 加键PB3需禁用 JTAG */ {GPIO_PIN_3, GPIOB, KEY_RESET}, /**< 加键PB3需禁用 JTAG */
{GPIO_PIN_15, GPIOD, KEY_RESET}, /**< 复位键PD15 */
}; };
#define KEY_COUNT (sizeof(key_configs) / sizeof(key_configs[0])) /**< 按键总数 */ #define KEY_COUNT (sizeof(key_configs) / sizeof(key_configs[0])) /**< 按键总数 */

View File

@@ -28,17 +28,17 @@
*/ */
typedef enum typedef enum
{ {
KEY_NONE = 0, /**< 无按键按下 */ KEY_NONE = 0, /**< 无按键按下 0*/
KEY_ENTER, /**< 确认键 */ KEY_ENTER, /**< 确认键 1*/
KEY_UP, /**< 向上键 */ KEY_UP, /**< 向上键 2*/
KEY_DOWN, /**< 向下键 */ KEY_DOWN, /**< 向下键 3*/
KEY_ESC, /**< 取消键 */ KEY_ESC, /**< 取消键 4*/
KEY_ADD, /**< 加键 */ KEY_ADD, /**< 加键 5*/
KEY_DEC, /**< 减键 */ KEY_DEC, /**< 减键 6*/
KEY_LEFT, /**< 向左键 */ KEY_LEFT, /**< 向左键 7*/
KEY_RIGHT, /**< 向右键 */ KEY_RIGHT, /**< 向右键 8*/
KEY_RESET, /**< 复位键 */ KEY_RESET, /**< 复位键 9*/
KEY_FACTORY, /**< 工厂模式键 */ KEY_FACTORY, /**< 工厂模式键 10*/
} KEY_TYPE; } KEY_TYPE;
/* ============================================================================ /* ============================================================================

View File

@@ -12,6 +12,7 @@
******************************************************************************/ ******************************************************************************/
#include "rs485.h" #include "rs485.h"
#include "160160D.h"
/* ============================================================================ /* ============================================================================
* USART 与引脚宏定义 * USART 与引脚宏定义
@@ -37,7 +38,6 @@
#define RS485_RX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) #define RS485_RX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)
#define BAUDRATE (700000) /**< 波特率 */ #define BAUDRATE (700000) /**< 波特率 */
/* ============================================================================ /* ============================================================================
* 全局变量定义 * 全局变量定义
* ============================================================================ */ * ============================================================================ */
@@ -45,7 +45,6 @@ RS485_REGISTER_TYPE RS485REG = {RESET, {0}, {0}}; /**< RS485 收发寄存器,
UART_HandleTypeDef rs485_handle; /**< HAL UART 句柄 */ UART_HandleTypeDef rs485_handle; /**< HAL UART 句柄 */
DMA_HandleTypeDef hdma_rs485_rx; /**< HAL DMA 接收句柄DMA1 Channel6 */ DMA_HandleTypeDef hdma_rs485_rx; /**< HAL DMA 接收句柄DMA1 Channel6 */
DMA_HandleTypeDef hdma_rs485_tx; /**< HAL DMA 发送句柄(未使用,保留) */
/* ============================================================================ /* ============================================================================
* 初始化函数 * 初始化函数
@@ -65,61 +64,201 @@ DMA_HandleTypeDef hdma_rs485_tx; /**< HAL DMA 发送句柄(未使用,保留
*/ */
void RS485_DMA_init(void) void RS485_DMA_init(void)
{ {
/* 使能时钟 */ /* ========================================================================
RS485_TX_GPIO_CLK_ENABLE(); * 第一步:使能相关外设时钟
RS485_RX_GPIO_CLK_ENABLE(); * ========================================================================
RS485_ENABLE_GPIO_CLK_ENABLE(); * 在 STM32 中,所有外设在使用前必须先使能其时钟,以降低功耗。
RS485_UX_CLK_ENABLE(); * 这里需要使能:
* - GPIOA 时钟:用于 TX(PA2)、RX(PA3)、DE(PA1) 引脚
* - USART2 时钟:用于串口通信功能
* - DMA1 时钟:用于 DMA 数据传输功能(必须!)
*/
RS485_TX_GPIO_CLK_ENABLE(); /* 使能 GPIOA 时钟TX 引脚 PA2 */
RS485_RX_GPIO_CLK_ENABLE(); /* 使能 GPIOA 时钟RX 引脚 PA3 */
RS485_ENABLE_GPIO_CLK_ENABLE(); /* 使能 GPIOA 时钟DE 引脚 PA1 */
RS485_UX_CLK_ENABLE(); /* 使能 USART2 时钟 */
__HAL_RCC_DMA1_CLK_ENABLE(); /* 使能 DMA1 时钟(必须!否则 DMA 无法工作) */
/* GPIO 初始化 */ /* ========================================================================
GPIO_InitTypeDef gpio_init_struct = {0}; * 第二步:配置 GPIO 引脚功能
gpio_init_struct.Pin = RS485_TX_GPIO_PIN; * ========================================================================
gpio_init_struct.Mode = GPIO_MODE_AF_PP; * RS485 需要三个 GPIO 引脚:
gpio_init_struct.Pull = GPIO_PULLUP; * 1. TX 引脚PA2发送数据配置为复用推挽输出
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; * 2. RX 引脚PA3接收数据配置为复用输入
* 3. DE 引脚PA1收发方向控制配置为普通推挽输出
*/
GPIO_InitTypeDef gpio_init_struct = {0}; /* 初始化 GPIO 配置结构体 */
/* 配置 TX 引脚PA2为复用推挽输出模式 */
gpio_init_struct.Pin = RS485_TX_GPIO_PIN; /* 选择 PA2 引脚 */
gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 复用推挽输出USART2_TX 功能 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉:提高输出驱动能力,抗干扰 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速:支持高波特率通信 */
HAL_GPIO_Init(RS485_TX_GPIO_PORT, &gpio_init_struct); HAL_GPIO_Init(RS485_TX_GPIO_PORT, &gpio_init_struct);
gpio_init_struct.Pin = RS485_RX_GPIO_PIN; /* 配置 RX 引脚PA3为复用输入模式 */
gpio_init_struct.Mode = GPIO_MODE_AF_INPUT; gpio_init_struct.Pin = RS485_RX_GPIO_PIN; /* 选择 PA3 引脚 */
gpio_init_struct.Mode = GPIO_MODE_AF_INPUT; /* 复用输入USART2_RX 功能 */
/* 注意复用输入模式下Pull 和 Speed 参数通常被忽略,但保持一致性 */
HAL_GPIO_Init(RS485_RX_GPIO_PORT, &gpio_init_struct); HAL_GPIO_Init(RS485_RX_GPIO_PORT, &gpio_init_struct);
gpio_init_struct.Pin = RS485_ENABLE_GPIO_PIN; /* 配置 DE 引脚PA1为普通推挽输出模式 */
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; gpio_init_struct.Pin = RS485_ENABLE_GPIO_PIN; /* 选择 PA1 引脚 */
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出:用于控制 RS485 收发方向 */
/* DE 引脚控制逻辑:
* - 低电平RESET使能发送模式允许数据从 TX 发送到总线
* - 高电平SET使能接收模式允许从总线接收数据到 RX
*/
HAL_GPIO_Init(RS485_ENABLE_GPIO_PORT, &gpio_init_struct); HAL_GPIO_Init(RS485_ENABLE_GPIO_PORT, &gpio_init_struct);
/* DMA 接收配置 */ /* ========================================================================
hdma_rs485_rx.Instance = DMA1_Channel6; * 第三步:配置 DMA 接收通道
hdma_rs485_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; * ========================================================================
hdma_rs485_rx.Init.PeriphInc = DMA_PINC_DISABLE; * DMADirect Memory Access用于在 USART 接收数据时自动将数据从
hdma_rs485_rx.Init.MemInc = DMA_MINC_ENABLE; * 外设寄存器传输到内存缓冲区,无需 CPU 干预,提高效率。
hdma_rs485_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; *
hdma_rs485_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; * STM32F103 中USART2_RX 对应 DMA1_Channel6
hdma_rs485_rx.Init.Mode = DMA_NORMAL; *
hdma_rs485_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH; * 时钟配置分析(系统时钟 72MHz
HAL_DMA_Init(&hdma_rs485_rx); * - AHB 时钟72MHzDMA 在 AHB 总线上)
* - APB1 时钟36MHzUSART2 在 APB1 总线上)
* - 波特率700000 bps
* - 每个字节时间:约 14.3 微秒10 位1 起始 + 8 数据 + 1 停止)
*
* DMA 传输速度要求:
* - DMA 需要从 APB1 外设USART2读取数据到 AHB 内存
* - 需要通过 AHB-APB1 桥,可能有 1-2 个时钟周期延迟
* - 理论上 DMA 时钟 72MHz 足够快,但总线仲裁可能导致延迟
*
* 溢出错误可能原因:
* 1. 总线竞争CPU 访问 APB1 外设时与 DMA 竞争总线
* 2. AHB-APB1 桥延迟DMA 访问 APB1 外设需要经过桥接器
* 3. DMA 优先级:虽然设置为最高,但总线仲裁是轮询的
* 4. 接收速度过快3208 字节连续接收DMA 可能来不及处理
*/
hdma_rs485_rx.Instance = DMA1_Channel6; /* 使用 DMA1 通道 6 */
hdma_rs485_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; /* 传输方向:外设到内存 */
hdma_rs485_rx.Init.PeriphInc = DMA_PINC_DISABLE; /* 外设地址不递增USART 数据寄存器地址固定 */
hdma_rs485_rx.Init.MemInc = DMA_MINC_ENABLE; /* 内存地址递增:数据依次存入缓冲区 */
hdma_rs485_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; /* 外设数据宽度字节8位 */
hdma_rs485_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; /* 内存数据宽度字节8位 */
hdma_rs485_rx.Init.Mode = DMA_NORMAL; /* 普通模式:传输完成后停止,需重新启动 */
hdma_rs485_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH; /* 最高优先级:确保数据及时传输,避免丢失 */
HAL_DMA_Init(&hdma_rs485_rx); /* 初始化 DMA 通道 */
/* 将 DMA 句柄关联到 UART 句柄,使 HAL 库能够自动管理 DMA 传输 */
__HAL_LINKDMA(&rs485_handle, hdmarx, hdma_rs485_rx); __HAL_LINKDMA(&rs485_handle, hdmarx, hdma_rs485_rx);
HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn); /* 使能 DMA 中断,用于接收完成等事件的通知 */
HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn); /* 使能 DMA1_Channel6 中断 */
HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 0, 0); /* 设置中断优先级为最高0,0 */
/* UART 初始化 */ /* ========================================================================
rs485_handle.Instance = RS485_UX; * 第四步:配置 UART 串口参数
rs485_handle.Init.BaudRate = BAUDRATE; * ========================================================================
rs485_handle.Init.WordLength = UART_WORDLENGTH_8B; * 配置 USART2 的通信参数,包括波特率、数据位、停止位、校验位等
rs485_handle.Init.StopBits = UART_STOPBITS_1; */
rs485_handle.Init.Parity = UART_PARITY_NONE; rs485_handle.Instance = RS485_UX; /* 使用 USART2 */
rs485_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE; rs485_handle.Init.BaudRate = BAUDRATE; /* 波特率700000 bps高速通信 */
rs485_handle.Init.Mode = UART_MODE_TX_RX; rs485_handle.Init.WordLength = UART_WORDLENGTH_8B; /* 数据位8 位 */
rs485_handle.Init.OverSampling = UART_OVERSAMPLING_16; rs485_handle.Init.StopBits = UART_STOPBITS_1; /* 停止位1 位 */
HAL_UART_Init(&rs485_handle); rs485_handle.Init.Parity = UART_PARITY_NONE; /* 校验位:无校验 */
rs485_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE; /* 硬件流控RS485 不需要 RTS/CTS */
rs485_handle.Init.Mode = UART_MODE_TX_RX; /* 工作模式:同时支持发送和接收 */
rs485_handle.Init.OverSampling = UART_OVERSAMPLING_16; /* 过采样16 倍STM32F103 仅支持 16 倍过采样) */
HAL_UART_Init(&rs485_handle); /* 初始化 UART应用上述配置 */
HAL_NVIC_EnableIRQ(RS485_UX_IRQn); /* 手动精确设置 BRR 寄存器以获得准确的 700000 波特率
HAL_NVIC_SetPriority(RS485_UX_IRQn, 1, 0); * 计算PCLK1 = 36MHz, 16倍过采样
* USARTDIV = 36000000 / (16 * 700000) = 3.2142857...
* BRR = (整数部分 << 4) | 小数部分
*/
/* 启动 DMA 接收 */ /* 使能 USART2 中断,用于处理接收完成、错误等事件 */
RS485_RECEIVE_ENABLE(); HAL_NVIC_EnableIRQ(RS485_UX_IRQn); /* 使能 USART2 中断 */
HAL_UARTEx_ReceiveToIdle_DMA(&rs485_handle, (uint8_t *)RS485REG.DR, UART_RX_LEN); HAL_NVIC_SetPriority(RS485_UX_IRQn, 1, 0); /* 设置中断优先级为 1低于 DMA 中断) */
/* ========================================================================
* 第五步:启动 DMA 接收
* ========================================================================
* 将 RS485 切换到接收模式,并启动 ReceiveToIdle DMA 接收
*
* ReceiveToIdle 模式特点:
* - 当检测到总线空闲IDLE自动触发接收完成回调
* - 适用于接收不定长数据帧(如 Modbus 协议)
* - 无需预先知道数据长度,总线空闲即表示一帧数据接收完成
*/
RS485_RECEIVE_ENABLE(); /* DE 引脚置高,切换到接收模式 */
// HAL_UARTEx_ReceiveToIdle_DMA(&rs485_handle, /* UART 句柄 */
// (uint8_t *)RS485REG.DR, /* 接收缓冲区RS485REG.DR3208 字节) */
// UART_RX_LEN); /* 最大接收长度3208 字节 */
HAL_UARTEx_ReceiveToIdle_IT(&rs485_handle, /* UART 句柄 */
(uint8_t *)RS485REG.DR, /* 接收缓冲区RS485REG.DR3208 字节) */
UART_RX_LEN); /* 最大接收长度3208 字节 */
/* 注意:接收完成后,会在 HAL_UARTEx_RxEventCallback 回调函数中
* 自动重新启动接收,实现连续接收 */
}
/**
* @brief RS485 反初始化DMA 接收模式)
* @note 按照与初始化相反的顺序清理资源:
* 1. 停止 DMA 接收
* 2. 禁用中断
* 3. 反初始化 DMA
* 4. 反初始化 UART
* 5. 反初始化 GPIO可选
* 注意:时钟通常不禁用,因为可能被其他外设使用
* @retval 无
*/
void RS485_DMA_DeInit(void)
{
/* ========================================================================
* 第一步:停止 DMA 接收
* ========================================================================
* 必须先停止正在进行的 DMA 传输,避免数据损坏
*/
HAL_UART_DMAStop(&rs485_handle); /* 停止 UART DMA 接收 */
/* ========================================================================
* 第二步:禁用中断
* ========================================================================
* 禁用 USART2 和 DMA1_Channel6 的中断,避免在反初始化过程中触发中断
*/
HAL_NVIC_DisableIRQ(RS485_UX_IRQn); /* 禁用 USART2 中断 */
HAL_NVIC_DisableIRQ(DMA1_Channel6_IRQn); /* 禁用 DMA1_Channel6 中断 */
/* ========================================================================
* 第三步:反初始化 DMA
* ========================================================================
* 断开 DMA 与 UART 的关联,然后反初始化 DMA 通道
* 注意HAL 库没有提供 __HAL_UNLINK_DMA 宏,需要手动将指针设为 NULL
*/
rs485_handle.hdmarx = NULL; /* 断开 DMA 与 UART 的关联 */
HAL_DMA_DeInit(&hdma_rs485_rx); /* 反初始化 DMA 通道 */
/* ========================================================================
* 第四步:反初始化 UART
* ========================================================================
* 反初始化 USART2这会调用 HAL_UART_MspDeInit 来清理底层硬件
*/
HAL_UART_DeInit(&rs485_handle); /* 反初始化 UART */
/* ========================================================================
* 第五步:反初始化 GPIO可选
* ========================================================================
* 将 GPIO 引脚恢复为默认状态(模拟输入,高阻态)
* 注意:如果这些引脚可能被其他外设使用,可以跳过此步骤
*/
HAL_GPIO_DeInit(RS485_TX_GPIO_PORT, RS485_TX_GPIO_PIN); /* 反初始化 TX 引脚PA2 */
HAL_GPIO_DeInit(RS485_RX_GPIO_PORT, RS485_RX_GPIO_PIN); /* 反初始化 RX 引脚PA3 */
HAL_GPIO_DeInit(RS485_ENABLE_GPIO_PORT, RS485_ENABLE_GPIO_PIN); /* 反初始化 DE 引脚PA1 */
/* 注意:时钟通常不禁用,因为:
* - GPIOA 时钟可能被其他外设使用
* - USART2 时钟可能被其他功能使用
* - DMA1 时钟可能被其他 DMA 通道使用
* 如果需要完全禁用时钟以节省功耗,可以在确认没有其他外设使用时禁用
*/
} }
/** /**
@@ -127,46 +266,96 @@ void RS485_DMA_init(void)
* @note 配置 GPIO、UART使能 RXNE 中断。不启用 DMA。 * @note 配置 GPIO、UART使能 RXNE 中断。不启用 DMA。
* 用于错误恢复或接收短帧。USART 时钟源需已配置。 * 用于错误恢复或接收短帧。USART 时钟源需已配置。
* @retval 无 * @retval 无
*
* @details 中断接收模式与 DMA 模式的区别:
* - 中断模式每接收一个字节触发一次中断CPU 需要逐字节处理
* - DMA 模式:自动将数据从 USART 传输到内存CPU 负担小
* 中断模式适用于:
* - DMA 故障时的错误恢复
* - 接收短帧(数据量小,中断开销可接受)
* - 调试和测试场景
*/ */
void RS485_init(void) void RS485_init(void)
{ {
/* 开启时钟 */ /* ========================================================================
RS485_TX_GPIO_CLK_ENABLE(); /* 使能串口TX脚时钟 */ * 第一步:使能相关外设时钟
RS485_RX_GPIO_CLK_ENABLE(); /* 使能串口RX脚时钟 */ * ========================================================================
RS485_ENABLE_GPIO_CLK_ENABLE(); /* 使能串口RX脚时钟 */ * 与 DMA 模式相同,需要使能 GPIOA 和 USART2 的时钟
RS485_UX_CLK_ENABLE(); /* 使能串口时钟 */ */
RS485_TX_GPIO_CLK_ENABLE(); /* 使能 GPIOA 时钟TX 引脚 PA2 */
RS485_RX_GPIO_CLK_ENABLE(); /* 使能 GPIOA 时钟RX 引脚 PA3 */
RS485_ENABLE_GPIO_CLK_ENABLE(); /* 使能 GPIOA 时钟DE 引脚 PA1 */
RS485_UX_CLK_ENABLE(); /* 使能 USART2 时钟 */
/* GPIO 初始化 */ /* ========================================================================
GPIO_InitTypeDef gpio_init_struct = {0}; * 第二步:配置 GPIO 引脚功能
gpio_init_struct.Pin = RS485_TX_GPIO_PIN; * ========================================================================
gpio_init_struct.Mode = GPIO_MODE_AF_PP; * GPIO 配置与 DMA 模式完全相同:
gpio_init_struct.Pull = GPIO_PULLUP; * - TX(PA2):复用推挽输出,用于发送数据
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; * - RX(PA3):复用输入,用于接收数据
* - DE(PA1):普通推挽输出,用于控制收发方向
*/
GPIO_InitTypeDef gpio_init_struct = {0}; /* 初始化 GPIO 配置结构体 */
/* 配置 TX 引脚PA2为复用推挽输出模式 */
gpio_init_struct.Pin = RS485_TX_GPIO_PIN; /* 选择 PA2 引脚 */
gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 复用推挽输出USART2_TX 功能 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉:提高输出驱动能力 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速:支持高波特率 */
HAL_GPIO_Init(RS485_TX_GPIO_PORT, &gpio_init_struct); HAL_GPIO_Init(RS485_TX_GPIO_PORT, &gpio_init_struct);
gpio_init_struct.Pin = RS485_RX_GPIO_PIN; /* 配置 RX 引脚PA3为复用输入模式 */
gpio_init_struct.Mode = GPIO_MODE_AF_INPUT; gpio_init_struct.Pin = RS485_RX_GPIO_PIN; /* 选择 PA3 引脚 */
gpio_init_struct.Mode = GPIO_MODE_AF_INPUT; /* 复用输入USART2_RX 功能 */
HAL_GPIO_Init(RS485_RX_GPIO_PORT, &gpio_init_struct); HAL_GPIO_Init(RS485_RX_GPIO_PORT, &gpio_init_struct);
gpio_init_struct.Pin = RS485_ENABLE_GPIO_PIN; /* 配置 DE 引脚PA1为普通推挽输出模式 */
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; gpio_init_struct.Pin = RS485_ENABLE_GPIO_PIN; /* 选择 PA1 引脚 */
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出:控制 RS485 收发方向 */
HAL_GPIO_Init(RS485_ENABLE_GPIO_PORT, &gpio_init_struct); HAL_GPIO_Init(RS485_ENABLE_GPIO_PORT, &gpio_init_struct);
/* UART 初始化 */ /* ========================================================================
rs485_handle.Instance = RS485_UX; * 第三步:配置 UART 串口参数
rs485_handle.Init.BaudRate = BAUDRATE; * ========================================================================
rs485_handle.Init.WordLength = UART_WORDLENGTH_8B; * UART 参数配置与 DMA 模式完全相同:
rs485_handle.Init.StopBits = UART_STOPBITS_1; * - 波特率700000 bps
rs485_handle.Init.Parity = UART_PARITY_NONE; * - 数据格式8 位数据位1 位停止位,无校验位
rs485_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE; * - 无硬件流控
rs485_handle.Init.Mode = UART_MODE_TX_RX; * - 16 倍过采样
rs485_handle.Init.OverSampling = UART_OVERSAMPLING_16; */
HAL_UART_Init(&rs485_handle); rs485_handle.Instance = RS485_UX; /* 使用 USART2 */
rs485_handle.Init.BaudRate = BAUDRATE; /* 波特率700000 bps */
rs485_handle.Init.WordLength = UART_WORDLENGTH_8B; /* 数据位8 位 */
rs485_handle.Init.StopBits = UART_STOPBITS_1; /* 停止位1 位 */
rs485_handle.Init.Parity = UART_PARITY_NONE; /* 校验位:无校验 */
rs485_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE; /* 硬件流控:无 */
rs485_handle.Init.Mode = UART_MODE_TX_RX; /* 工作模式:发送和接收 */
rs485_handle.Init.OverSampling = UART_OVERSAMPLING_16; /* 过采样16 倍 */
HAL_UART_Init(&rs485_handle); /* 初始化 UART */
__HAL_UART_ENABLE_IT(&rs485_handle, UART_IT_RXNE); /* ========================================================================
HAL_NVIC_EnableIRQ(RS485_UX_IRQn); * 第四步使能接收中断RXNE
HAL_NVIC_SetPriority(RS485_UX_IRQn, 1, 0); * ========================================================================
RS485_RECEIVE_ENABLE(); * RXNEReceive Not Empty中断
* - 当 USART 接收数据寄存器RDR中有新数据时触发
* - 每接收一个字节触发一次中断
* - 在中断服务函数中需要手动读取数据并存入缓冲区
*
* 注意:此模式不使用 DMA需要 CPU 在中断中逐字节处理数据
*/
__HAL_UART_ENABLE_IT(&rs485_handle, UART_IT_RXNE); /* 使能 RXNE接收数据寄存器非空中断 */
HAL_NVIC_EnableIRQ(RS485_UX_IRQn); /* 使能 USART2 中断 */
HAL_NVIC_SetPriority(RS485_UX_IRQn, 1, 0); /* 设置中断优先级为 1 */
/* ========================================================================
* 第五步:切换到接收模式
* ========================================================================
* 将 DE 引脚置高,使 RS485 处于接收状态,准备接收数据
*/
RS485_RECEIVE_ENABLE(); /* DE 引脚置高,切换到接收模式 */
/* 注意:中断模式下,需要在 USART2_IRQHandler 中断服务函数中
* 手动调用 HAL_UART_IRQHandler并在回调函数中处理接收到的数据 */
} }
/* ============================================================================ /* ============================================================================
@@ -178,14 +367,70 @@ void RS485_init(void)
* @param huart UART 句柄 * @param huart UART 句柄
* @param Size 本帧接收到的字节数 * @param Size 本帧接收到的字节数
* @note 当为 RS485 所用 UART 时:置位 NewMessageFlag并重新启动 ReceiveToIdle DMA。 * @note 当为 RS485 所用 UART 时:置位 NewMessageFlag并重新启动 ReceiveToIdle DMA。
*
* @details HAL 库在调用此回调前已经完成以下操作:
* - DMA 状态已设置为 READY在 DMA IRQ 处理中)
* - UART 状态已设置为 READY在 UART_DMAReceiveCplt 中)
* - DMA 请求已禁用(清除 DMAR 位)
* - 相关中断已禁用
* 因此可以直接调用 HAL_UARTEx_ReceiveToIdle_DMA 重新启动接收。
*
* @warning 重要:当接收数据长度 = 缓冲区大小3208字节
* - DMA 传输完成中断触发,而不是 IDLE 中断
* - 如果发生溢出错误OREHAL 库会中止 DMA 并重置 ReceptionType 为 STANDARD
* - 这会导致回调函数不会被调用
* - 解决方案:添加错误回调函数来处理这种情况
*
* @retval 无 * @retval 无
*/ */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{ {
if (huart->Instance == RS485_UX) if (huart->Instance == RS485_UX)
{ {
/* 调试信息:显示接收到的字节数 */
// HZ12AndChar_Printf(0,32, (uint8_t*)"RxEvent:", RESET);
// IntValue_Printf(32,32, Size, RESET);
/* 设置新消息标志,通知主程序处理数据 */
RS485REG.NewMessageFlag = SET; RS485REG.NewMessageFlag = SET;
HAL_UARTEx_ReceiveToIdle_DMA(&rs485_handle, (uint8_t *)RS485REG.DR, UART_RX_LEN);
/* 重新启动 IT 接收*/
HAL_UARTEx_ReceiveToIdle_IT(&rs485_handle, /* UART 句柄 */
(uint8_t *)RS485REG.DR, /* 接收缓冲区RS485REG.DR3208 字节) */
UART_RX_LEN); /* 最大接收长度3208 字节 */
}
}
/**
* @brief UART 错误回调函数
* @param huart UART 句柄
* @note 当发生 UART 错误如溢出、帧错误等HAL 库会调用此函数
*
* @details 重要当接收3208字节时如果发生溢出错误OREHAL 库会:
* 1. 中止 DMA 传输
* 2. 重置 ReceptionType 为 STANDARD
* 3. 调用错误回调函数(而不是 RxEventCallback
* 因此,需要在错误回调中处理这种情况,并重新启动接收
*
* @details 错误代码说明:
* - HAL_UART_ERROR_NONE (0x00): 无错误
* - HAL_UART_ERROR_PE (0x01): 奇偶校验错误
* - HAL_UART_ERROR_NE (0x02): 噪声错误(信号干扰、波特率不匹配等)
* - HAL_UART_ERROR_FE (0x04): 帧错误(停止位检测失败)
* - HAL_UART_ERROR_ORE (0x08): 溢出错误数据接收过快DMA 来不及处理)
* - HAL_UART_ERROR_DMA (0x10): DMA 传输错误
*
* @retval 无
*/
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == RS485_UX)
{
// /* 调试信息:显示错误代码 */
// HZ12AndChar_Printf(0,64, (uint8_t*)"Error:", RESET);
// IntValue_Printf(32,64, huart->ErrorCode, RESET);
//HAL_UARTEx_ReceiveToIdle_DMA(&rs485_handle, (uint8_t *)RS485REG.DR, UART_RX_LEN);
} }
} }

View File

@@ -20,7 +20,7 @@
/* ============================================================================ /* ============================================================================
* 宏定义 * 宏定义
* ============================================================================ */ * ============================================================================ */
#define UART_RX_LEN (3208) /**< 接收缓冲区长度(字节),与 Modbus 数据帧长度一致 */ #define UART_RX_LEN (4208) /**< 接收缓冲区长度(字节),与 Modbus 数据帧长度一致 */
#define UART_TX_LEN (8) /**< 发送缓冲区长度(字节) */ #define UART_TX_LEN (8) /**< 发送缓冲区长度(字节) */
/* ============================================================================ /* ============================================================================
@@ -39,7 +39,7 @@ typedef struct
} RS485_REGISTER_TYPE; } RS485_REGISTER_TYPE;
extern RS485_REGISTER_TYPE RS485REG; /**< 全局 RS485 收发寄存器 */ extern RS485_REGISTER_TYPE RS485REG; /**< 全局 RS485 收发寄存器 */
extern UART_HandleTypeDef rs485_handle;
/* ============================================================================ /* ============================================================================
* 函数声明 * 函数声明
* ============================================================================ */ * ============================================================================ */
@@ -51,6 +51,13 @@ extern RS485_REGISTER_TYPE RS485REG; /**< 全局 RS485 收发寄存器 */
*/ */
void RS485_DMA_init(void); void RS485_DMA_init(void);
/**
* @brief RS485 反初始化DMA 接收模式)
* @note 按照与初始化相反的顺序清理资源:停止 DMA、禁用中断、反初始化 DMA/UART/GPIO。
* @retval 无
*/
void RS485_DMA_DeInit(void);
/** /**
* @brief RS485 初始化(中断接收模式) * @brief RS485 初始化(中断接收模式)
* @note 配置 GPIO、UART使能 RXNE 中断接收。用于异常恢复或短帧接收。 * @note 配置 GPIO、UART使能 RXNE 中断接收。用于异常恢复或短帧接收。

View File

@@ -1,29 +1,29 @@
/** /**
**************************************************************************************************** ****************************************************************************************************
* @file delay.c * @file delay.c
* @author 正点原子团队(ALIENTEK) * @author 正点原子团队(ALIENTEK)
* @version V1.1 * @version V1.1
* @date 2023-02-25 * @date 2023-02-25
* @brief 使用SysTick的普通计数模式对延迟进行管理(支持ucosii) * @brief 使用SysTick的普通计数模式对延迟进行管理(支持ucosii)
* 提供delay_init初始化函数 delay_us和delay_ms等延时函数 * 提供delay_init初始化函数 delay_us和delay_ms等延时函数
* @license Copyright (c) 2022-2032, 广州市星翼电子科技有限公司 * @license Copyright (c) 2022-2032, 广州市星翼电子科技有限公司
**************************************************************************************************** ****************************************************************************************************
* @attention * @attention
* *
* 实验平台:正点原子 STM32F103开发板 * 实验平台:正点原子 STM32F103开发板
* 在线视频:www.yuanzige.com * 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com * 技术论坛:www.openedv.com
* 公司网址:www.alientek.com * 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com * 购买地址:openedv.taobao.com
* *
* 修改说明 * 修改说明
* V1.0 20230206 * V1.0 20230206
* 第一次发布 * 第一次发布
* V1.1 20230225 * V1.1 20230225
* 修改SYS_SUPPORT_OS部分代码, 默认仅支持UCOSII 2.93.01版本, 其他OS请参考实现 * 修改SYS_SUPPORT_OS部分代码, 默认仅支持UCOSII 2.93.01版本, 其他OS请参考实现
* 修改delay_init不再使用8分频,全部统一使用MCU时钟 * 修改delay_init不再使用8分频,全部统一使用MCU时钟
* 修改delay_us使用时钟摘取法延时, 兼容OS * 修改delay_us使用时钟摘取法延时, 兼容OS
* 修改delay_ms直接使用delay_us延时实现. * 修改delay_ms直接使用delay_us延时实现.
* *
**************************************************************************************************** ****************************************************************************************************
*/ */
@@ -32,78 +32,78 @@
#include "./SYSTEM/delay/delay.h" #include "./SYSTEM/delay/delay.h"
static uint32_t g_fac_us = 0; /* us延时倍乘数 */ static uint32_t g_fac_us = 0; /* us延时倍乘数 */
/* 如果SYS_SUPPORT_OS定义了,说明要支持OS了(不限于UCOS) */ /* 如果SYS_SUPPORT_OS定义了,说明要支持OS了(不限于UCOS) */
#if SYS_SUPPORT_OS #if SYS_SUPPORT_OS
/* 添加公共头文件 ( ucos需要用到) */ /* 添加公共头文件 ( ucos需要用到) */
#include "os.h" #include "os.h"
/* 定义g_fac_ms变量, 表示ms延时的倍乘数, 代表每个节拍的ms数, (仅在使能os的时候,需要用到) */ /* 定义g_fac_ms变量, 表示ms延时的倍乘数, 代表每个节拍的ms数, (仅在使能os的时候,需要用到) */
static uint16_t g_fac_ms = 0; static uint16_t g_fac_ms = 0;
/* /*
* 当delay_us/delay_ms需要支持OS的时候需要三个与OS相关的宏定义和函数来支持 * 当delay_us/delay_ms需要支持OS的时候需要三个与OS相关的宏定义和函数来支持
* 首先是3个宏定义: * 首先是3个宏定义:
* delay_osrunning :用于表示OS当前是否正在运行,以决定是否可以使用相关函数 * delay_osrunning :用于表示OS当前是否正在运行,以决定是否可以使用相关函数
* delay_ostickspersec:用于表示OS设定的时钟节拍,delay_init将根据这个参数来初始化systick * delay_ostickspersec:用于表示OS设定的时钟节拍,delay_init将根据这个参数来初始化systick
* delay_osintnesting :用于表示OS中断嵌套级别,因为中断里面不可以调度,delay_ms使用该参数来决定如何运行 * delay_osintnesting :用于表示OS中断嵌套级别,因为中断里面不可以调度,delay_ms使用该参数来决定如何运行
* 然后是3个函数: * 然后是3个函数:
* delay_osschedlock :用于锁定OS任务调度,禁止调度 * delay_osschedlock :用于锁定OS任务调度,禁止调度
* delay_osschedunlock:用于解锁OS任务调度,重新开启调度 * delay_osschedunlock:用于解锁OS任务调度,重新开启调度
* delay_ostimedly :用于OS延时,可以引起任务调度. * delay_ostimedly :用于OS延时,可以引起任务调度.
* *
* 本例程仅作UCOSII的支持,其他OS,请自行参考着移植 * 本例程仅作UCOSII的支持,其他OS,请自行参考着移植
*/ */
/* 支持UCOSII */ /* 支持UCOSII */
#define delay_osrunning OSRunning /* OS是否运行标记,0,不运行;1,在运行 */ #define delay_osrunning OSRunning /* OS是否运行标记,0,不运行;1,在运行 */
#define delay_ostickspersec OS_TICKS_PER_SEC /* OS时钟节拍,即每秒调度次数 */ #define delay_ostickspersec OS_TICKS_PER_SEC /* OS时钟节拍,即每秒调度次数 */
#define delay_osintnesting OSIntNesting /* 中断嵌套级别,即中断嵌套次数 */ #define delay_osintnesting OSIntNesting /* 中断嵌套级别,即中断嵌套次数 */
/** /**
* @brief us级延时时,关闭任务调度(防止打断us级延迟) * @brief us级延时时,关闭任务调度(防止打断us级延迟)
* @param 无 * @param 无
* @retval 无 * @retval 无
*/ */
void delay_osschedlock(void) void delay_osschedlock(void)
{ {
OSSchedLock(); /* UCOSII的方式,禁止调度防止打断us延时 */ OSSchedLock(); /* UCOSII的方式,禁止调度防止打断us延时 */
} }
/** /**
* @brief us级延时时,恢复任务调度 * @brief us级延时时,恢复任务调度
* @param 无 * @param 无
* @retval 无 * @retval 无
*/ */
void delay_osschedunlock(void) void delay_osschedunlock(void)
{ {
OSSchedUnlock(); /* UCOSII的方式,恢复调度 */ OSSchedUnlock(); /* UCOSII的方式,恢复调度 */
} }
/** /**
* @brief us级延时时,恢复任务调度 * @brief us级延时时,恢复任务调度
* @param ticks: 延时的节拍数 * @param ticks: 延时的节拍数
* @retval 无 * @retval 无
*/ */
void delay_ostimedly(uint32_t ticks) void delay_ostimedly(uint32_t ticks)
{ {
OSTimeDly(ticks); /* UCOSII延时 */ OSTimeDly(ticks); /* UCOSII延时 */
} }
/** /**
* @brief systick中断服务函数,使用OS时用到 * @brief systick中断服务函数,使用OS时用到
* @param ticks : 延时的节拍数 * @param ticks : 延时的节拍数
* @retval 无 * @retval 无
*/ */
void SysTick_Handler(void) void SysTick_Handler(void)
{ {
/* OS 开始跑了,才执行正常的调度处理 */ /* OS 开始跑了,才执行正常的调度处理 */
if (delay_osrunning == OS_TRUE) if (delay_osrunning == OS_TRUE)
{ {
/* 调用 uC/OS-II 的 SysTick 中断服务函数 */ /* 调用 uC/OS-II 的 SysTick 中断服务函数 */
OS_CPU_SysTickHandler(); OS_CPU_SysTickHandler();
} }
HAL_IncTick(); HAL_IncTick();
@@ -111,47 +111,47 @@ void SysTick_Handler(void)
#endif #endif
/** /**
* @brief 初始化延迟函数 * @brief 初始化延迟函数
* @param sysclk: 系统时钟频率, 即CPU频率(rcc_c_ck), 72MHz * @param sysclk: 系统时钟频率, 即CPU频率(rcc_c_ck), 72MHz
* @retval 无 * @retval 无
*/ */
void delay_init(uint16_t sysclk) void delay_init(uint16_t sysclk)
{ {
#if SYS_SUPPORT_OS /* 如果需要支持OS */ #if SYS_SUPPORT_OS /* 如果需要支持OS */
uint32_t reload; uint32_t reload;
#endif #endif
g_fac_us = sysclk; /* 由于在HAL_Init中已对systick做了配置所以这里无需重新配置 */ g_fac_us = sysclk; /* 由于在HAL_Init中已对systick做了配置所以这里无需重新配置 */
#if SYS_SUPPORT_OS /* 如果需要支持OS. */ #if SYS_SUPPORT_OS /* 如果需要支持OS. */
reload = sysclk; /* 每秒钟的计数次数 单位为M */ reload = sysclk; /* 每秒钟的计数次数 单位为M */
reload *= 1000000 / delay_ostickspersec; /* 根据delay_ostickspersec设定溢出时间,reload为24位 reload *= 1000000 / delay_ostickspersec; /* 根据delay_ostickspersec设定溢出时间,reload为24位
* 寄存器,最大值:16777216,在72M下,约合0.233s左右 * 寄存器,最大值:16777216,在72M下,约合0.233s左右
*/ */
g_fac_ms = 1000 / delay_ostickspersec; /* 代表OS可以延时的最少单位 */ g_fac_ms = 1000 / delay_ostickspersec; /* 代表OS可以延时的最少单位 */
SysTick->CTRL |= 1 << 1; /* 开启SYSTICK中断 */ SysTick->CTRL |= 1 << 1; /* 开启SYSTICK中断 */
SysTick->LOAD = reload; /* 每1/delay_ostickspersec秒中断一次 */ SysTick->LOAD = reload; /* 每1/delay_ostickspersec秒中断一次 */
SysTick->CTRL |= 1 << 0; /* 开启SYSTICK */ SysTick->CTRL |= 1 << 0; /* 开启SYSTICK */
#endif #endif
} }
/** /**
* @brief 延时nus * @brief 延时nus
* @note 无论是否使用OS, 都是用时钟摘取法来做us延时 * @note 无论是否使用OS, 都是用时钟摘取法来做us延时
* @param nus: 要延时的us数 * @param nus: 要延时的us数
* @note nus取值范围: 0 ~ (2^32 / fac_us) (fac_us一般等于系统主频, 自行套入计算) * @note nus取值范围: 0 ~ (2^32 / fac_us) (fac_us一般等于系统主频, 自行套入计算)
* @retval 无 * @retval 无
*/ */
void delay_us(uint32_t nus) void delay_us(uint32_t nus)
{ {
uint32_t ticks; uint32_t ticks;
uint32_t told, tnow, tcnt = 0; uint32_t told, tnow, tcnt = 0;
uint32_t reload = SysTick->LOAD; /* LOAD的值 */ uint32_t reload = SysTick->LOAD; /* LOAD的值 */
ticks = nus * g_fac_us; /* 需要的节拍数 */ ticks = nus * g_fac_us; /* 需要的节拍数 */
#if SYS_SUPPORT_OS /* 如果需要支持OS */ #if SYS_SUPPORT_OS /* 如果需要支持OS */
delay_osschedlock(); /* 锁定 OS 的任务调度器 */ delay_osschedlock(); /* 锁定 OS 的任务调度器 */
#endif #endif
told = SysTick->VAL; /* 刚进入时的计数器值 */ told = SysTick->VAL; /* 刚进入时的计数器值 */
while (1) while (1)
{ {
tnow = SysTick->VAL; tnow = SysTick->VAL;
@@ -159,7 +159,7 @@ void delay_us(uint32_t nus)
{ {
if (tnow < told) if (tnow < told)
{ {
tcnt += told - tnow; /* 这里注意一下SYSTICK是一个递减的计数器就可以了 */ tcnt += told - tnow; /* 这里注意一下SYSTICK是一个递减的计数器就可以了 */
} }
else else
{ {
@@ -168,44 +168,44 @@ void delay_us(uint32_t nus)
told = tnow; told = tnow;
if (tcnt >= ticks) if (tcnt >= ticks)
{ {
break; /* 时间超过/等于要延迟的时间,则退出 */ break; /* 时间超过/等于要延迟的时间,则退出 */
} }
} }
} }
#if SYS_SUPPORT_OS /* 如果需要支持OS */ #if SYS_SUPPORT_OS /* 如果需要支持OS */
delay_osschedunlock(); /* 恢复 OS 的任务调度器 */ delay_osschedunlock(); /* 恢复 OS 的任务调度器 */
#endif #endif
} }
/** /**
* @brief 延时nms * @brief 延时nms
* @param nms: 要延时的ms数 (0< nms <= (2^32 / fac_us / 1000))(fac_us一般等于系统主频, 自行套入计算) * @param nms: 要延时的ms数 (0< nms <= (2^32 / fac_us / 1000))(fac_us一般等于系统主频, 自行套入计算)
* @retval 无 * @retval 无
*/ */
void delay_ms(uint16_t nms) void delay_ms(uint16_t nms)
{ {
#if SYS_SUPPORT_OS /* 如果需要支持OS, 则根据情况调用os延时以释放CPU */ #if SYS_SUPPORT_OS /* 如果需要支持OS, 则根据情况调用os延时以释放CPU */
if (delay_osrunning && delay_osintnesting == 0) /* 如果OS已经在跑了,并且不是在中断里面(中断里面不能任务调度) */ if (delay_osrunning && delay_osintnesting == 0) /* 如果OS已经在跑了,并且不是在中断里面(中断里面不能任务调度) */
{ {
if (nms >= g_fac_ms) /* 延时的时间大于OS的最少时间周期 */ if (nms >= g_fac_ms) /* 延时的时间大于OS的最少时间周期 */
{ {
delay_ostimedly(nms / g_fac_ms); /* OS延时 */ delay_ostimedly(nms / g_fac_ms); /* OS延时 */
} }
nms %= g_fac_ms; /* OS已经无法提供这么小的延时了,采用普通方式延时 */ nms %= g_fac_ms; /* OS已经无法提供这么小的延时了,采用普通方式延时 */
} }
#endif #endif
delay_us((uint32_t)(nms * 1000)); /* 普通方式延时 */ delay_us((uint32_t)(nms * 1000)); /* 普通方式延时 */
} }
/** /**
* @brief HAL库内部函数用到的延时 * @brief HAL库内部函数用到的延时
* @note HAL库的延时默认用Systick如果我们没有开Systick的中断会导致调用这个延时后无法退出 * @note HAL库的延时默认用Systick如果我们没有开Systick的中断会导致调用这个延时后无法退出
* @param Delay : 要延时的毫秒数 * @param Delay : 要延时的毫秒数
* @retval None * @retval None
*/ */
void HAL_Delay(uint32_t Delay) void HAL_Delay(uint32_t Delay)

View File

@@ -1,23 +1,23 @@
/** /**
**************************************************************************************************** ****************************************************************************************************
* @file sys.c * @file sys.c
* @author 正点原子团队(ALIENTEK) * @author 正点原子团队(ALIENTEK)
* @version V1.0 * @version V1.0
* @date 2020-04-17 * @date 2020-04-17
* @brief 系统初始化代码(包括时钟配置/中断管理/GPIO设置等) * @brief 系统初始化代码(包括时钟配置/中断管理/GPIO设置等)
* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司 * @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
**************************************************************************************************** ****************************************************************************************************
* @attention * @attention
* *
* 实验平台:正点原子 STM32F103开发板 * 实验平台:正点原子 STM32F103开发板
* 在线视频:www.yuanzige.com * 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com * 技术论坛:www.openedv.com
* 公司网址:www.alientek.com * 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com * 购买地址:openedv.taobao.com
* *
* 修改说明 * 修改说明
* V1.0 20211103 * V1.0 20211103
* 第一次发布 * 第一次发布
* *
**************************************************************************************************** ****************************************************************************************************
*/ */
@@ -26,21 +26,21 @@
/** /**
* @brief 设置中断向量表偏移地址 * @brief 设置中断向量表偏移地址
* @param baseaddr: 基址 * @param baseaddr: 基址
* @param offset: 偏移量(必须是0, 或者0X100的倍数) * @param offset: 偏移量(必须是0, 或者0X100的倍数)
* @retval 无 * @retval 无
*/ */
void sys_nvic_set_vector_table(uint32_t baseaddr, uint32_t offset) void sys_nvic_set_vector_table(uint32_t baseaddr, uint32_t offset)
{ {
/* 设置NVIC的向量表偏移寄存器,VTOR低9位保留,即[8:0]保留 */ /* 设置NVIC的向量表偏移寄存器,VTOR低9位保留,即[8:0]保留 */
SCB->VTOR = baseaddr | (offset & (uint32_t)0xFFFFFE00); SCB->VTOR = baseaddr | (offset & (uint32_t)0xFFFFFE00);
} }
/** /**
* @brief 执行: WFI指令(执行完该指令进入低功耗状态, 等待中断唤醒) * @brief 执行: WFI指令(执行完该指令进入低功耗状态, 等待中断唤醒)
* @param 无 * @param 无
* @retval 无 * @retval 无
*/ */
void sys_wfi_set(void) void sys_wfi_set(void)
{ {
@@ -48,9 +48,9 @@ void sys_wfi_set(void)
} }
/** /**
* @brief 关闭所有中断(但是不包括fault和NMI中断) * @brief 关闭所有中断(但是不包括fault和NMI中断)
* @param 无 * @param 无
* @retval 无 * @retval 无
*/ */
void sys_intx_disable(void) void sys_intx_disable(void)
{ {
@@ -58,9 +58,9 @@ void sys_intx_disable(void)
} }
/** /**
* @brief 开启所有中断 * @brief 开启所有中断
* @param 无 * @param 无
* @retval 无 * @retval 无
*/ */
void sys_intx_enable(void) void sys_intx_enable(void)
{ {
@@ -68,31 +68,31 @@ void sys_intx_enable(void)
} }
/** /**
* @brief 设置栈顶地址 * @brief 设置栈顶地址
* @note 左侧的红X, 属于MDK误报, 实际是没问题的 * @note 左侧的红X, 属于MDK误报, 实际是没问题的
* @param addr: 栈顶地址 * @param addr: 栈顶地址
* @retval 无 * @retval 无
*/ */
void sys_msr_msp(uint32_t addr) void sys_msr_msp(uint32_t addr)
{ {
__set_MSP(addr); /* 设置栈顶地址 */ __set_MSP(addr); /* 设置栈顶地址 */
} }
/** /**
* @brief 进入待机模式 * @brief 进入待机模式
* @param 无 * @param 无
* @retval 无 * @retval 无
*/ */
void sys_standby(void) void sys_standby(void)
{ {
__HAL_RCC_PWR_CLK_ENABLE(); /* 使能电源时钟 */ __HAL_RCC_PWR_CLK_ENABLE(); /* 使能电源时钟 */
SET_BIT(PWR->CR, PWR_CR_PDDS); /* 进入待机模式 */ SET_BIT(PWR->CR, PWR_CR_PDDS); /* 进入待机模式 */
} }
/** /**
* @brief 系统软复位 * @brief 系统软复位
* @param 无 * @param 无
* @retval 无 * @retval 无
*/ */
void sys_soft_reset(void) void sys_soft_reset(void)
{ {
@@ -100,10 +100,10 @@ void sys_soft_reset(void)
} }
/** /**
* @brief 系统时钟初始化函数 * @brief 系统时钟初始化函数
* @param plln: PLL倍频系数(PLL倍频), 取值范围: 2~16 * @param plln: PLL倍频系数(PLL倍频), 取值范围: 2~16
中断向量表位置在启动时已经在SystemInit()中初始化 中断向量表位置在启动时已经在SystemInit()中初始化
* @retval 无 * @retval 无
*/ */
void sys_stm32_clock_init(uint32_t plln) void sys_stm32_clock_init(uint32_t plln)
{ {
@@ -111,30 +111,30 @@ void sys_stm32_clock_init(uint32_t plln)
RCC_OscInitTypeDef rcc_osc_init = {0}; RCC_OscInitTypeDef rcc_osc_init = {0};
RCC_ClkInitTypeDef rcc_clk_init = {0}; RCC_ClkInitTypeDef rcc_clk_init = {0};
rcc_osc_init.OscillatorType = RCC_OSCILLATORTYPE_HSE; /* 选择要配置HSE */ rcc_osc_init.OscillatorType = RCC_OSCILLATORTYPE_HSE; /* 选择要配置HSE */
rcc_osc_init.HSEState = RCC_HSE_ON; /* 打开HSE */ rcc_osc_init.HSEState = RCC_HSE_ON; /* 打开HSE */
rcc_osc_init.HSEPredivValue = RCC_HSE_PREDIV_DIV1; /* HSE预分频系数 */ rcc_osc_init.HSEPredivValue = RCC_HSE_PREDIV_DIV1; /* HSE预分频系数 */
rcc_osc_init.PLL.PLLState = RCC_PLL_ON; /* 打开PLL */ rcc_osc_init.PLL.PLLState = RCC_PLL_ON; /* 打开PLL */
rcc_osc_init.PLL.PLLSource = RCC_PLLSOURCE_HSE; /* PLL时钟源选择HSE */ rcc_osc_init.PLL.PLLSource = RCC_PLLSOURCE_HSE; /* PLL时钟源选择HSE */
rcc_osc_init.PLL.PLLMUL = plln; /* PLL倍频系数 */ rcc_osc_init.PLL.PLLMUL = plln; /* PLL倍频系数 */
ret = HAL_RCC_OscConfig(&rcc_osc_init); /* 初始化 */ ret = HAL_RCC_OscConfig(&rcc_osc_init); /* 初始化 */
if (ret != HAL_OK) if (ret != HAL_OK)
{ {
while (1); /* 时钟初始化失败,之后的程序将可能无法正常执行,可以在这里加入自己的处理 */ while (1); /* 时钟初始化失败,之后的程序将可能无法正常执行,可以在这里加入自己的处理 */
} }
/* 选中PLL作为系统时钟源并且配置HCLK,PCLK1和PCLK2*/ /* 选中PLL作为系统时钟源并且配置HCLK,PCLK1和PCLK2*/
rcc_clk_init.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2); rcc_clk_init.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
rcc_clk_init.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; /* 设置系统时钟来自PLL */ rcc_clk_init.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; /* 设置系统时钟来自PLL */
rcc_clk_init.AHBCLKDivider = RCC_SYSCLK_DIV1; /* AHB分频系数为1 */ rcc_clk_init.AHBCLKDivider = RCC_SYSCLK_DIV1; /* AHB分频系数为1 */
rcc_clk_init.APB1CLKDivider = RCC_HCLK_DIV2; /* APB1分频系数为2 */ rcc_clk_init.APB1CLKDivider = RCC_HCLK_DIV2; /* APB1分频系数为2 */
rcc_clk_init.APB2CLKDivider = RCC_HCLK_DIV1; /* APB2分频系数为1 */ rcc_clk_init.APB2CLKDivider = RCC_HCLK_DIV1; /* APB2分频系数为1 */
ret = HAL_RCC_ClockConfig(&rcc_clk_init, FLASH_LATENCY_2); /* 同时设置FLASH延时周期为2WS也就是3个CPU周期。 */ ret = HAL_RCC_ClockConfig(&rcc_clk_init, FLASH_LATENCY_2); /* 同时设置FLASH延时周期为2WS也就是3个CPU周期。 */
if (ret != HAL_OK) if (ret != HAL_OK)
{ {
while (1); /* 时钟初始化失败,之后的程序将可能无法正常执行,可以在这里加入自己的处理 */ while (1); /* 时钟初始化失败,之后的程序将可能无法正常执行,可以在这里加入自己的处理 */
} }
} }

View File

@@ -256,28 +256,22 @@ void NL_LOGO_Printf(void)
* 2. 其他图片 * 2. 其他图片
* @retval 无 * @retval 无
*/ */
uint8_t RS485_Process(KEY_TYPE key, uint8_t flag) void RS485_Process(KEY_TYPE key)
{ {
static KEY_TYPE key_bk = KEY_NONE; /* 按键备份(静态变量,保持状态) */ static KEY_TYPE key_bk = KEY_NONE; /* 按键备份(静态变量,保持状态) */
static uint8_t LED_BUFF[4]; /* LED状态缓冲区静态变量 */ static uint8_t LED_BUFF[4]; /* LED状态缓冲区静态变量 */
static uint8_t info[8]; /* 命令信息缓冲区(静态变量) */ static uint8_t info[8]; /* 命令信息缓冲区(静态变量) */
static uint32_t CRC_ERR_COUNT = 0; /* CRC错误计数静态变量 */ static uint32_t CRC_ERR_COUNT = 0; /* CRC错误计数静态变量 */
uint8_t ConnectFlg = flag;
/* 按键处理:保存按键值 */ /* 按键处理:保存按键值 */
if(key != KEY_NONE) if(key != KEY_NONE)
{
if(key_bk != key) /* 按键值变化时更新备份 */
{ {
key_bk = key; key_bk = key;
} }
}
if(RS485REG.NewMessageFlag) /* 接收到新消息 */ if(RS485REG.NewMessageFlag) /* 接收到新消息 */
{ {
RS485REG.NewMessageFlag = RESET; /* 清除控制字 */ RS485REG.NewMessageFlag = RESET; /* 清除控制字 */
ConnectFlg = 1; /* 设置连接标志 */
if(Check_CRC((uint8_t*)&RS485REG.DR[0], UART_RX_LEN) == TRUE) /* CRC校验通过 */ if(Check_CRC((uint8_t*)&RS485REG.DR[0], UART_RX_LEN) == TRUE) /* CRC校验通过 */
{ {
CRC_ERR_COUNT = 0; /* 重置CRC错误计数 */ CRC_ERR_COUNT = 0; /* 重置CRC错误计数 */
@@ -313,7 +307,10 @@ uint8_t RS485_Process(KEY_TYPE key, uint8_t flag)
if(CRC_ERR_COUNT >= 3) /* 连续3次CRC错误 */ if(CRC_ERR_COUNT >= 3) /* 连续3次CRC错误 */
{ {
/* 接收错位,重新初始化接收 */ /* 接收错位,重新初始化接收 */
RS485_DMA_init(); /* 重新初始化*/ // RS485_DMA_DeInit();
// delay_ms(500);
// delay_ms(500);
// RS485_DMA_init(); /* 重新初始化*/
CRC_ERR_COUNT = 0; /* 重置CRC错误计数 */ CRC_ERR_COUNT = 0; /* 重置CRC错误计数 */
} }
} }
@@ -337,7 +334,6 @@ uint8_t RS485_Process(KEY_TYPE key, uint8_t flag)
if(RS485POLL.RT_count > RS485_RT_COUNT_MAX) /* 定时到*/ if(RS485POLL.RT_count > RS485_RT_COUNT_MAX) /* 定时到*/
{ {
RefreshScreen(RT_CMD, key_bk); /* 发送实时刷新命令 */ RefreshScreen(RT_CMD, key_bk); /* 发送实时刷新命令 */
key_bk = KEY_NONE; /* 清除按键备份 */
} }
} }
@@ -352,12 +348,9 @@ uint8_t RS485_Process(KEY_TYPE key, uint8_t flag)
/* 连续超时处理:应答超时次数达到上限 */ /* 连续超时处理:应答超时次数达到上限 */
if((RS485POLL.ACK_OverTimeCnt >= RS485_ACK_OVERTIME_MAX)) /* 连续超时次数达到上限 */ if((RS485POLL.ACK_OverTimeCnt >= RS485_ACK_OVERTIME_MAX)) /* 连续超时次数达到上限 */
{ {
ConnectFlg = 0;
RS485POLL.ACK_OverTimeCnt = RS485_ACK_OVERTIME_MAX; /* 限制最大计数(防止溢出) */ RS485POLL.ACK_OverTimeCnt = RS485_ACK_OVERTIME_MAX; /* 限制最大计数(防止溢出) */
RS485_DMA_init(); /* 重新初始化RS485 */
RS485POLL.CMD_ACK = SET; /* 设置命令应答标志(允许重新发送) */ RS485POLL.CMD_ACK = SET; /* 设置命令应答标志(允许重新发送) */
} }
return ConnectFlg;
} }
/** /**

View File

@@ -25,12 +25,11 @@
/** /**
* @brief Modbus 通信主处理函数(远程图片处理) * @brief Modbus 通信主处理函数(远程图片处理)
* @param key 按键值KEY_TYPE用于上报按键事件 * @param key 按键值KEY_TYPE用于上报按键事件
* @param flag 连接标志(传入当前连接状态,用于更新)
* @note 处理接收帧、按键上报、定时刷新、应答超时等;采用 138 端处理图片、 * @note 处理接收帧、按键上报、定时刷新、应答超时等;采用 138 端处理图片、
* 整体上传显示的方式刷图。需在周期任务中调用。 * 整体上传显示的方式刷图。需在周期任务中调用。
* @retval 更新后的连接标志1=已连接0=未连接/超时) * @retval
*/ */
uint8_t RS485_Process(KEY_TYPE key, uint8_t flag); void RS485_Process(KEY_TYPE key);
/** /**
* @brief 从 Flash 读取 LOGO 并显示 * @brief 从 Flash 读取 LOGO 并显示

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -12,7 +12,7 @@
<lExt>*.lib</lExt> <lExt>*.lib</lExt>
<tExt>*.txt; *.h; *.inc; *.md</tExt> <tExt>*.txt; *.h; *.inc; *.md</tExt>
<pExt>*.plm</pExt> <pExt>*.plm</pExt>
<CppX>*.cpp</CppX> <CppX>*.cpp; *.cc; *.cxx</CppX>
<nMigrate>0</nMigrate> <nMigrate>0</nMigrate>
</Extensions> </Extensions>
@@ -117,35 +117,15 @@
<pMon>STLink\ST-LINKIII-KEIL_SWO.dll</pMon> <pMon>STLink\ST-LINKIII-KEIL_SWO.dll</pMon>
</DebugOpt> </DebugOpt>
<TargetDriverDllRegistry> <TargetDriverDllRegistry>
<SetRegEntry>
<Number>0</Number>
<Key>UL2CM3</Key>
<Name>UL2CM3(-S0 -C0 -P0 ) -FN1 -FC1000 -FD20000000 -FF0STM32F10x_512 -FL080000 -FS08000000 -FP0($$Device:STM32F103ZE$Flash\STM32F10x_512.FLM)</Name>
</SetRegEntry>
<SetRegEntry> <SetRegEntry>
<Number>0</Number> <Number>0</Number>
<Key>ST-LINKIII-KEIL_SWO</Key> <Key>ST-LINKIII-KEIL_SWO</Key>
<Name>-U12029401190032524B413836 -O206 -SF10000 -C0 -A0 -I0 -HNlocalhost -HP7184 -P1 -N00("ARM CoreSight SW-DP (ARM Core") -D00(1BA01477) -L00(0) -TO131090 -TC10000000 -TT10000000 -TP21 -TDS8000 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -FO15 -FD20000000 -FC1000 -FN1 -FF0STM32F10x_512.FLM -FS08000000 -FL080000 -FP0($$Device:STM32F103ZE$Flash\STM32F10x_512.FLM)</Name> <Name>-U12029401190032524B413836 -O206 -SF4000 -C0 -A0 -I0 -HNlocalhost -HP7184 -P1 -N00("ARM CoreSight SW-DP") -D00(1BA01477) -L00(0) -TO131090 -TC10000000 -TT10000000 -TP21 -TDS8007 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -FO15 -FD20000000 -FC1000 -FN1 -FF0STM32F10x_512.FLM -FS08000000 -FL080000 -FP0($$Device:STM32F103ZE$Flash\STM32F10x_512.FLM)</Name>
</SetRegEntry> </SetRegEntry>
<SetRegEntry> <SetRegEntry>
<Number>0</Number> <Number>0</Number>
<Key>ARMRTXEVENTFLAGS</Key> <Key>UL2CM3</Key>
<Name>-L70 -Z18 -C0 -M0 -T1</Name> <Name>UL2CM3(-S0 -C0 -P0 -FD20000000 -FC1000 -FN1 -FF0STM32F10x_512 -FS08000000 -FL080000 -FP0($$Device:STM32F103ZE$Flash\STM32F10x_512.FLM))</Name>
</SetRegEntry>
<SetRegEntry>
<Number>0</Number>
<Key>DLGTARM</Key>
<Name>(1010=-1,-1,-1,-1,0)(1007=-1,-1,-1,-1,0)(1008=-1,-1,-1,-1,0)(1009=-1,-1,-1,-1,0)</Name>
</SetRegEntry>
<SetRegEntry>
<Number>0</Number>
<Key>ARMDBGFLAGS</Key>
<Name></Name>
</SetRegEntry>
<SetRegEntry>
<Number>0</Number>
<Key>DLGUARM</Key>
<Name>(105=-1,-1,-1,-1,0)</Name>
</SetRegEntry> </SetRegEntry>
</TargetDriverDllRegistry> </TargetDriverDllRegistry>
<Breakpoint/> <Breakpoint/>
@@ -154,13 +134,13 @@
</Tracepoint> </Tracepoint>
<DebugFlag> <DebugFlag>
<trace>0</trace> <trace>0</trace>
<periodic>1</periodic> <periodic>0</periodic>
<aLwin>1</aLwin> <aLwin>0</aLwin>
<aCover>0</aCover> <aCover>0</aCover>
<aSer1>0</aSer1> <aSer1>0</aSer1>
<aSer2>0</aSer2> <aSer2>0</aSer2>
<aPa>0</aPa> <aPa>0</aPa>
<viewmode>1</viewmode> <viewmode>0</viewmode>
<vrSel>0</vrSel> <vrSel>0</vrSel>
<aSym>0</aSym> <aSym>0</aSym>
<aTbox>0</aTbox> <aTbox>0</aTbox>
@@ -223,7 +203,7 @@
<Group> <Group>
<GroupName>User</GroupName> <GroupName>User</GroupName>
<tvExp>1</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel> <cbSel>0</cbSel>
<RteFlg>0</RteFlg> <RteFlg>0</RteFlg>
@@ -279,7 +259,7 @@
<Group> <Group>
<GroupName>Drivers/SYSTEM</GroupName> <GroupName>Drivers/SYSTEM</GroupName>
<tvExp>0</tvExp> <tvExp>1</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel> <cbSel>0</cbSel>
<RteFlg>0</RteFlg> <RteFlg>0</RteFlg>

View File

@@ -16,8 +16,8 @@
<TargetCommonOption> <TargetCommonOption>
<Device>STM32F103ZE</Device> <Device>STM32F103ZE</Device>
<Vendor>STMicroelectronics</Vendor> <Vendor>STMicroelectronics</Vendor>
<PackID>Keil.STM32F1xx_DFP.2.4.1</PackID> <PackID>Keil.STM32F1xx_DFP.2.3.0</PackID>
<PackURL>https://www.keil.com/pack/</PackURL> <PackURL>http://www.keil.com/pack/</PackURL>
<Cpu>IRAM(0x20000000,0x00010000) IROM(0x08000000,0x00080000) CPUTYPE("Cortex-M3") CLOCK(12000000) ELITTLE</Cpu> <Cpu>IRAM(0x20000000,0x00010000) IROM(0x08000000,0x00080000) CPUTYPE("Cortex-M3") CLOCK(12000000) ELITTLE</Cpu>
<FlashUtilSpec></FlashUtilSpec> <FlashUtilSpec></FlashUtilSpec>
<StartupFile></StartupFile> <StartupFile></StartupFile>

View File

@@ -18,21 +18,13 @@
#include "main.h" #include "main.h"
/* ============================================================================
* 宏定义
* ============================================================================ */
#define TIMER 5 /**< 定时器周期(毫秒) */
#define LED_TOGGLE_TIME 1000 /**< 系统运行指示灯闪烁周期(毫秒),即 LED 点亮持续时间 */
/* ============================================================================ /* ============================================================================
* 全局变量定义 * 全局变量定义
* ============================================================================ */ * ============================================================================ */
volatile static uint32_t sysRunTime = 0; /**< 系统运行时间计数器单位5ms */ volatile static uint32_t sysRunTime = 0; /**< 系统运行时间计数器单位5ms */
volatile static KEY_TYPE keyStatus = KEY_NONE; /**< 按键状态指示(当前按键值) */
volatile static KEY_TYPE keyStatusBack = KEY_NONE; /**< 按键状态指示(当前按键值) */
volatile static uint8_t LED_ToggleFlag = 0; /**< LED 闪烁定时标志位1=需要翻转) */ volatile static uint8_t LED_ToggleFlag = 0; /**< LED 闪烁定时标志位1=需要翻转) */
static KEY_TYPE keyStatus = KEY_NONE; /**< 按键状态指示(当前按键值) */
static uint8_t ConnectFlg = 0; /**< 连接标志1=已连接0=未连接 */
volatile static uint32_t KEY_RUN_Count = 0; /**< 按键按下时 LCD 屏幕显示计时计数器单位5ms */
/* ============================================================================ /* ============================================================================
* 内部函数实现 * 内部函数实现
* ============================================================================ */ * ============================================================================ */
@@ -51,32 +43,8 @@ volatile static uint32_t KEY_RUN_Count = 0; /**< 按键按下时 LCD 屏
static void Key_ProcessCallback(KEY_TYPE key_type) static void Key_ProcessCallback(KEY_TYPE key_type)
{ {
BackLight_ON(); /**< 任意按键按下,都打开背光 */ BackLight_ON(); /**< 任意按键按下,都打开背光 */
KeyRun_Disp(1); /**< 有按键按下LCD 显示按键指示 */
KEY_RUN_Count = 0; /**< 重置按键显示计时器 */
keyStatus = key_type; /**< 更新按键状态,供主循环处理 */ keyStatus = key_type; /**< 更新按键状态,供主循环处理 */
} }
/**
* @brief 按键显示自动关闭处理
* @note 在定时器中断中调用,用于自动关闭按键按下后的 LCD 显示指示
* 处理逻辑:
* - KEY_RUN_Count 递增(每 5ms 一次)
* - 当计数达到 15约 75ms关闭按键显示
* - 计数限制在 15防止溢出
* @retval 无
*/
void KeyRun_Disp_Close(void)
{
KEY_RUN_Count++;
if (KEY_RUN_Count >= 15)
{
KEY_RUN_Count = 15; /**< 限制最大计数,防止溢出 */
KeyRun_Disp(0); /**< 关闭按键显示指示 */
}
}
/* ============================================================================ /* ============================================================================
* 主程序入口 * 主程序入口
* ============================================================================ */ * ============================================================================ */
@@ -128,28 +96,29 @@ int main(void)
/* ========== 通信与显示初始化 ========== */ /* ========== 通信与显示初始化 ========== */
Process_Init(); /**< Modbus 通信任务初始化 */ Process_Init(); /**< Modbus 通信任务初始化 */
NL_LOGO_Printf(); /**< 显示存储的 LOGO从 Flash 读取) */
/* ========== 定时器与回调注册 ========== */ /* ========== 定时器与回调注册 ========== */
gtim_timx_int_init(50-1, 7200-1); /**< 定时器初始化5ms 周期50×0.1ms7200 分频) */ gtim_timx_int_init(50-1, 7200-1); /**< 定时器初始化5ms 周期50×0.1ms7200 分频) */
Key_RegisterCallback(Key_ProcessCallback); /**< 注册按键回调函数 */ Key_RegisterCallback(Key_ProcessCallback); /**< 注册按键回调函数 */
/* ========== 主循环 ========== */ /* ========== 主循环 ========== */
while (1) while (1)
{
/* LED 指示灯闪烁控制(仅在已连接状态下闪烁) */
if (ConnectFlg == 1)
{ {
if (LED_ToggleFlag == 1) if (LED_ToggleFlag == 1)
{ {
LED_ToggleFlag = 0; /**< 清除闪烁标志 */ LED_ToggleFlag = 0; /**< 清除闪烁标志 */
LED_Toggle(LED_RUN); /**< 翻转运行指示灯 */ // LED_Toggle(15); /**< 翻转运行指示灯 */
} }
/* 快速的处理 keyStatus 的状态,避免与中断处理冲突*/
if(keyStatus != KEY_NONE)
{
keyStatusBack = keyStatus;
keyStatus = KEY_NONE;
} }
/* RS485 Modbus 通信处理(包含按键上报) */ /* RS485 Modbus 通信处理(包含按键上报) */
ConnectFlg = RS485_Process(keyStatus, ConnectFlg); RS485_Process(keyStatusBack);
keyStatus = KEY_NONE; /**< 清除按键状态(已处理) */ keyStatusBack = KEY_NONE;
/* 系统运行时间监控与自动复位保护 */ /* 系统运行时间监控与自动复位保护 */
if (sysRunTime > 1000000) /**< 约 5000 秒1000000 × 5ms */ if (sysRunTime > 1000000) /**< 约 5000 秒1000000 × 5ms */
@@ -157,6 +126,7 @@ int main(void)
HAL_NVIC_SystemReset(); /**< 系统立即复位(防止长时间运行异常) */ HAL_NVIC_SystemReset(); /**< 系统立即复位(防止长时间运行异常) */
sysRunTime = 0; sysRunTime = 0;
} }
} }
} }
@@ -185,18 +155,15 @@ void GTIM_TIMX_INT_IRQHandler(void)
if (__HAL_TIM_GET_FLAG(&g_timx_handle, TIM_FLAG_UPDATE) != RESET) if (__HAL_TIM_GET_FLAG(&g_timx_handle, TIM_FLAG_UPDATE) != RESET)
{ {
sysRunTime++; /**< 系统运行时间计数递增 */ sysRunTime++; /**< 系统运行时间计数递增 */
/* LED 闪烁标志更新:每 200 次中断1 秒)翻转一次 */ /* LED 闪烁标志更新:每 200 次中断1 秒)翻转一次 */
if (sysRunTime % (LED_TOGGLE_TIME / 5) == 0) if (sysRunTime % 200 == 0)
{ {
LED_ToggleFlag = 1; /**< 设置 LED 闪烁标志 */ LED_ToggleFlag = 1; /**< 设置 LED 闪烁标志 */
} }
Button_Ticks(); /**< 按键检测扫描MultiButton 库) */ Button_Ticks(); /**< 按键检测扫描MultiButton 库) */
Clear_Watchdog(); /**< 看门狗喂狗 */ Clear_Watchdog(); /**< 看门狗喂狗 */
BackLight_Close(); /**< 背光关闭检测 */ BackLight_Close(); /**< 背光关闭检测 */
Process_Count(); /**< Modbus 通信轮询计数 */ Process_Count(); /**< Modbus 通信轮询计数 */
KeyRun_Disp_Close(); /**< LCD 屏幕按键指示自动关闭处理 */
__HAL_TIM_CLEAR_IT(&g_timx_handle, TIM_IT_UPDATE); /**< 清除定时器溢出中断标志 */ __HAL_TIM_CLEAR_IT(&g_timx_handle, TIM_IT_UPDATE); /**< 清除定时器溢出中断标志 */
} }