调试key LCD rs485都正常运行,修复了bug,但是DMA接收的问题没有解决
This commit is contained in:
@@ -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整除 */
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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])) /**< 按键总数 */
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
|
|||||||
@@ -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;
|
* DMA(Direct 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 时钟:72MHz(DMA 在 AHB 总线上)
|
||||||
|
* - APB1 时钟:36MHz(USART2 在 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.DR(3208 字节) */
|
||||||
|
// UART_RX_LEN); /* 最大接收长度:3208 字节 */
|
||||||
|
HAL_UARTEx_ReceiveToIdle_IT(&rs485_handle, /* UART 句柄 */
|
||||||
|
(uint8_t *)RS485REG.DR, /* 接收缓冲区:RS485REG.DR(3208 字节) */
|
||||||
|
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();
|
* RXNE(Receive 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 中断
|
||||||
|
* - 如果发生溢出错误(ORE),HAL 库会中止 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.DR(3208 字节) */
|
||||||
|
UART_RX_LEN); /* 最大接收长度:3208 字节 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief UART 错误回调函数
|
||||||
|
* @param huart UART 句柄
|
||||||
|
* @note 当发生 UART 错误(如溢出、帧错误等)时,HAL 库会调用此函数
|
||||||
|
*
|
||||||
|
* @details 重要:当接收3208字节时,如果发生溢出错误(ORE),HAL 库会:
|
||||||
|
* 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 中断接收。用于异常恢复或短帧接收。
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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); /* 时钟初始化失败,之后的程序将可能无法正常执行,可以在这里加入自己的处理 */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,9 +36,9 @@
|
|||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
* 轮询与超时常量
|
* 轮询与超时常量
|
||||||
* ============================================================================ */
|
* ============================================================================ */
|
||||||
#define RS485_RT_COUNT_MAX (55) /**< 实时轮询计数上限,约 275ms(55×5ms) */
|
#define RS485_RT_COUNT_MAX (55) /**< 实时轮询计数上限,约 275ms(55×5ms) */
|
||||||
#define RS485_ACK_COUNT_MAX (40) /**< 应答超时计数上限,约 200ms(40×5ms) */
|
#define RS485_ACK_COUNT_MAX (40) /**< 应答超时计数上限,约 200ms(40×5ms) */
|
||||||
#define RS485_ACK_OVERTIME_MAX (5) /**< 连续应答超时次数上限,超过则重初始化 RS485 */
|
#define RS485_ACK_OVERTIME_MAX (5) /**< 连续应答超时次数上限,超过则重初始化 RS485 */
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
* 数据结构定义
|
* 数据结构定义
|
||||||
@@ -256,40 +256,34 @@ 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错误计数 */
|
||||||
memcpy(Picture, (uint8_t*)RS485REG.DR, 3200); /* 提取图片数据(前3200字节) */
|
memcpy(Picture, (uint8_t*)RS485REG.DR, 3200); /* 提取图片数据(前3200字节) */
|
||||||
memcpy(info, (uint8_t*)RS485REG.DR + 3200, 8); /* 提取命令信息(后8字节) */
|
memcpy(info, (uint8_t*)RS485REG.DR + 3200, 8); /* 提取命令信息(后8字节) */
|
||||||
|
|
||||||
if(info[4] == 0x89 && info[5] == 0x45) /* LOGO图片命令(特殊标识) */
|
if(info[4] == 0x89 && info[5] == 0x45) /* LOGO图片命令(特殊标识) */
|
||||||
{
|
{
|
||||||
memcpy(logo.bmpdata, Picture, 3200); /* 复制图片到LOGO结构体 */
|
memcpy(logo.bmpdata, Picture, 3200); /* 复制图片到LOGO结构体 */
|
||||||
logo.biWidth = info[0] << 8 | info[1]; /* 解析图片宽度(高字节左移8位 + 低字节) */
|
logo.biWidth = info[0] << 8 | info[1]; /* 解析图片宽度(高字节左移8位 + 低字节) */
|
||||||
logo.biHeight = info[2] << 8 | info[3]; /* 解析图片高度(高字节左移8位 + 低字节) */
|
logo.biHeight = info[2] << 8 | info[3]; /* 解析图片高度(高字节左移8位 + 低字节) */
|
||||||
BMP_SAVE2False(); /* 保存 BMP 图片到 Flash */
|
BMP_SAVE2False(); /* 保存 BMP 图片到 Flash */
|
||||||
|
|
||||||
/* 显示LOGO */
|
/* 显示LOGO */
|
||||||
BackLight_ON(); /* 开启背光 */
|
BackLight_ON(); /* 开启背光 */
|
||||||
@@ -309,11 +303,14 @@ uint8_t RS485_Process(KEY_TYPE key, uint8_t flag)
|
|||||||
}
|
}
|
||||||
else /* CRC校验失败 */
|
else /* CRC校验失败 */
|
||||||
{
|
{
|
||||||
CRC_ERR_COUNT++; /* CRC错误计数递增 */
|
CRC_ERR_COUNT++; /* CRC错误计数递增 */
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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 并显示
|
||||||
|
|||||||
2265
Output/DTU.hex
2265
Output/DTU.hex
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
67
Users/main.c
67
Users/main.c
@@ -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.1ms,7200 分频) */
|
gtim_timx_int_init(50-1, 7200-1); /**< 定时器初始化:5ms 周期(50×0.1ms,7200 分频) */
|
||||||
Key_RegisterCallback(Key_ProcessCallback); /**< 注册按键回调函数 */
|
Key_RegisterCallback(Key_ProcessCallback); /**< 注册按键回调函数 */
|
||||||
|
|
||||||
|
|
||||||
/* ========== 主循环 ========== */
|
/* ========== 主循环 ========== */
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
/* LED 指示灯闪烁控制(仅在已连接状态下闪烁) */
|
if (LED_ToggleFlag == 1)
|
||||||
if (ConnectFlg == 1)
|
|
||||||
{
|
{
|
||||||
if (LED_ToggleFlag == 1)
|
LED_ToggleFlag = 0; /**< 清除闪烁标志 */
|
||||||
{
|
// LED_Toggle(15); /**< 翻转运行指示灯 */
|
||||||
LED_ToggleFlag = 0; /**< 清除闪烁标志 */
|
}
|
||||||
LED_Toggle(LED_RUN); /**< 翻转运行指示灯 */
|
/* 快速的处理 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +147,7 @@ int main(void)
|
|||||||
*
|
*
|
||||||
* 定时器配置:5ms 周期
|
* 定时器配置:5ms 周期
|
||||||
* 中断标志:TIM_FLAG_UPDATE(定时器溢出标志)
|
* 中断标志:TIM_FLAG_UPDATE(定时器溢出标志)
|
||||||
* @retval 无
|
* @retval 无
|
||||||
*/
|
*/
|
||||||
void GTIM_TIMX_INT_IRQHandler(void)
|
void GTIM_TIMX_INT_IRQHandler(void)
|
||||||
{
|
{
|
||||||
@@ -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); /**< 清除定时器溢出中断标志 */
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user