1.添加了必要的注释和readme文件以提高代码可读性和项目文档化。2.增加了按键按下时LCD屏幕显示功能

This commit is contained in:
2026-01-25 15:42:48 +08:00
parent 373b31adfd
commit 6a861cee4e
30 changed files with 2311 additions and 50645 deletions

View File

@@ -1319,28 +1319,45 @@ void FillBoxScreen(uint8_t x, uint8_t y, uint8_t len, uint8_t high, uint8_t byte
y++; /* 移动到下一行 */
}
}
/**
* @brief LCD 控制与背光 GPIO 初始化
* @note 初始化 UC1698U 控制器所需的 GPIO 引脚,分为两组:
* GPIOB 组(控制信号与背光):
* | 引脚 | 功能 | 说明 |
* |------|----------|----------------|
* | PB0 | 背光 | 背光开关 |
* | PB10 | RST | 复位信号 |
* | PB11 | CS | 片选 |
* | PB12 | RD | 读使能 |
* | PB13 | WR | 写使能 |
* | PB14 | CD | 命令/数据选择 |
* GPIOE 组(数据总线):
* | 引脚 | 功能 | 说明 |
* |-----------|----------|-------------------------|
* | PE8~PE15 | D0~D7 | 8 位并行数据总线 |
* 配置为推挽输出、上拉、高速模式
* @retval 无
*/
void LCD_GPIO_Init(void)
{
GPIO_InitTypeDef gpio_init_struct;
__HAL_RCC_GPIOB_CLK_ENABLE(); // 时钟初始化
__HAL_RCC_GPIOE_CLK_ENABLE(); // 时钟初始化
__HAL_RCC_GPIOB_CLK_ENABLE(); /**< 使能 GPIOB 时钟 */
__HAL_RCC_GPIOE_CLK_ENABLE(); /**< 使能 GPIOE 时钟 */
// 配置 LCD 引脚
/* ========== GPIOB控制信号与背光 ========== */
gpio_init_struct.Pin = GPIO_PIN_14 | GPIO_PIN_13 | GPIO_PIN_12 | GPIO_PIN_11 | GPIO_PIN_10 | GPIO_PIN_0;
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /*推挽输出模式*/
gpio_init_struct.Pull = GPIO_PULLUP;
gpio_init_struct.Speed = GPIO_SPEED_HIGH;
// 初始化 LCD 选引脚
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /**< 推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /**< 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_HIGH; /**< 高速 */
HAL_GPIO_Init(GPIOB, &gpio_init_struct);
// 配置 LCD 引脚
/* ========== GPIOE8 位数据总线 ========== */
gpio_init_struct.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /*推挽输出模式*/
gpio_init_struct.Pull = GPIO_PULLUP;
gpio_init_struct.Speed = GPIO_SPEED_HIGH;
// 初始化 LCD 选引脚
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /**< 推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /**< 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_HIGH; /**< 高速 */
HAL_GPIO_Init(GPIOE, &gpio_init_struct);
}
/**
@@ -1422,64 +1439,66 @@ void DisplayNANRUI_LOGO(void)
}
/*******************************************************************************
* FunctionName : Display_Picture()
* Description : 图片显示程序
* EntryParameter : none
* ReturnValue : none
*******************************************************************************/
/**
* @brief 在 LCD 上显示指定尺寸的位图
* @param Width 位图宽度(像素,不超过 160
* @param Height 位图高度(像素,不超过 160
* @param picture 位图数据指针(每像素 1 位,每行 Width/8 字节,行内取反显示)
* @note 使用 4K 色RGB444模式位图居中显示
* X 坐标按每 3 像素递增;每行末尾补 0 使点数能被 3 整除
* 若 Width 或 Height 大于 160 则直接返回
* @retval 无
*/
void Display_BMP(uint32_t Width, uint32_t Height, const uint8_t* picture)
{
uint8_t i,n;
uint8_t disp_point[4]={0x00,0x0f,0xf0,0xff};
uint8_t x, y;
uint32_t bytesNumOfRow;
set_color_mode(COLOR_4K_444);//设置4.4.4.模式
if(Width>160 || Height>160)
return ; //设置4.4.4.模式
x = ((160-Width)/2)/3; //x坐标为每3个像素点递增
y = (160-Height)/2 + Height; //x坐标为每3个像素点递增
bytesNumOfRow = Width/8;
for(i=0;i<Height;i++)
{
SetAddress(x, y-i);
for(n=0;n<bytesNumOfRow;n++)
{
WriteData(disp_point[(~picture[i*bytesNumOfRow+n]>>6)&0x03]);
WriteData(disp_point[(~picture[i*bytesNumOfRow+n]>>4)&0x03]);
WriteData(disp_point[(~picture[i*bytesNumOfRow+n]>>2)&0x03]);
WriteData(disp_point[(~picture[i*bytesNumOfRow+n]>>0)&0x03]);
}
WriteData(0x00); //补全每行末尾的数据,使总点数能被三整除
}
uint8_t i, n;
uint8_t disp_point[4] = {0x00, 0x0f, 0xf0, 0xff}; /**< 4K 色像素值映射 */
uint8_t x, y;
uint32_t bytesNumOfRow;
set_color_mode(COLOR_4K_444); /**< 设置 4K 色模式 */
if (Width > 160 || Height > 160)
return;
x = ((160 - Width) / 2) / 3; /**< 居中 X每 3 像素为一单位 */
y = (160 - Height) / 2 + Height; /**< 居中 Y自下而上绘制 */
bytesNumOfRow = Width / 8;
for (i = 0; i < Height; i++)
{
SetAddress(x, y - i);
for (n = 0; n < bytesNumOfRow; n++)
{
WriteData(disp_point[(~picture[i * bytesNumOfRow + n] >> 6) & 0x03]);
WriteData(disp_point[(~picture[i * bytesNumOfRow + n] >> 4) & 0x03]);
WriteData(disp_point[(~picture[i * bytesNumOfRow + n] >> 2) & 0x03]);
WriteData(disp_point[(~picture[i * bytesNumOfRow + n] >> 0) & 0x03]);
}
WriteData(0x00); /**< 补足使每行点数能被 3 整除 */
}
}
/*******************************************************************************
* FunctionName : DisplayNANRUI_BMP()
* Description : BMP格式显示测试
* EntryParameter : none
* ReturnValue : none
*******************************************************************************/
/**
* @brief 显示南瑞NANRUILogo 位图
* @note 调用 Display_BMP尺寸 128×50 像素,数据源为 NANRUI_BMP
* @retval 无
*/
void DisplayNANRUI_BMP(void)
{
const uint32_t xmax=128, ymax=50;
Display_BMP(xmax, ymax, NANRUI_BMP);
const uint32_t xmax = 128, ymax = 50;
Display_BMP(xmax, ymax, NANRUI_BMP);
}
/*******************************************************************************
* FunctionName : DisplayNANRUI_BMP()
* Description : BMP格式显示测试
* EntryParameter : none
* ReturnValue : none
*******************************************************************************/
/**
* @brief 显示 QQ Logo 位图
* @note 调用 Display_BMP尺寸 96×94 像素,数据源为 QQ_BMP
* @retval 无
*/
void DisplayQQ_BMP(void)
{
const uint32_t xmax=96, ymax=94;
Display_BMP(xmax, ymax, QQ_BMP);
const uint32_t xmax = 96, ymax = 94;
Display_BMP(xmax, ymax, QQ_BMP);
}
/**
* @brief 绘制单条水平线(内部函数)
@@ -1589,58 +1608,6 @@ void DiaplayHeadGraph(void)
/* 绘制左侧垂直边框从第18行开始高度142 */
DisplayVerticalLine(0, 18, 142, 0x80);
}
/**
* @brief 绘制带阴影效果的方框(内部函数)
* @param x 起始列坐标X坐标
* @param y 起始行坐标Y坐标
* @param len 方框宽度(像素单位)
* @param high 方框高度(行数)
* @note 绘制一个带3D阴影效果的方框
* 包括:左边框、上边框、下边框(带阴影)、右边框(带渐变阴影效果)
* @retval 无
*/
static void DrawBox(uint8_t x, uint8_t y, uint8_t len, uint8_t high)
{
/* 绘制左边框 */
DisplayVerticalLine(x, y, high, 0x80);
/* 绘制上边框 */
DisplayHorizontalLine(x, y, len);
/* 绘制下边框 */
DisplayHorizontalLine(x, y + high, len);
/* 绘制第一层阴影 */
writebyte(x, y + high + 1, 0x7F, RESET);
DisplayHorizontalLine(x + 1, y + high + 1, len);
/* 绘制第二层阴影 */
writebyte(x, y + high + 2, 0x3F, RESET);
DisplayHorizontalLine(x + 1, y + high + 2, len);
/* 绘制右边框(带渐变阴影效果) */
DisplayVerticalLine(x + len, y, high + 3, 0x80); /* 主边框 */
DisplayVerticalLine(x + len, y + 1, high + 2, 0xC0); /* 第一层阴影 */
DisplayVerticalLine(x + len, y + 2, high + 1, 0xE0); /* 第二层阴影 */
}
/**
* @brief 绘制无阴影效果的方框(内部函数)
* @param x 起始列坐标X坐标
* @param y 起始行坐标Y坐标
* @param len 方框宽度(像素单位)
* @param high 方框高度(行数)
* @note 绘制一个简单的方框,无阴影效果
* 包括:左边框、上边框、下边框、右边框
* @retval 无
*/
static void DrawBox_NoShadow(uint8_t x, uint8_t y, uint8_t len, uint8_t high)
{
DisplayVerticalLine(x, y, high, 0x80); /* 左边框 */
DisplayHorizontalLine(x, y, len); /* 上边框 */
DisplayHorizontalLine(x, y + high, len); /* 下边框 */
DisplayVerticalLine(x + len - 1, y, high, 0x20); /* 右边框 */
}
/**
* @brief 绘制一个像素点(带渐变效果)
* @param x 列坐标X坐标
@@ -1665,168 +1632,3 @@ void DrawPoint(uint8_t x, uint8_t y)
DisplayVerticalLine(x + 1, y + 4, 1, 0x60);
DisplayHorizontalLine(x + 1, y + 5, 1);
}
/**
* @brief 显示消息提示框(静态模式)
* @note 在屏幕指定位置显示一个带阴影的消息提示框
* 位置:坐标(10,40)尺寸33x50像素
* 先清除背景区域,然后绘制方框
* 动态框模式代码已注释需要定义DYNAMIC_BOX
* @retval 无
*/
void MessageBox(void)
{
/* 静态框模式 */
FillBoxScreen(9, 37, 35, 59, 0x00); /* 清除背景区域 */
DrawBox(10, 40, 33, 50); /* 绘制带阴影的方框 */
}
/**
* @brief 清除消息提示框
* @note 清除消息提示框的整个显示区域
* 位置和尺寸与MessageBox函数对应
* @retval 无
*/
void ClrMessageBox(void)
{
FillBoxScreen(9, 37, 35, 59, 0x00); /* 清除消息框区域 */
}
/**
* @brief 清除消息提示框内容区域
* @note 只清除消息框内部的内容区域,保留边框
* 用于刷新消息内容而不重新绘制边框
* @retval 无
*/
void ClrMessageBoxContent(void)
{
FillBoxScreen(11, 41, 31, 48, 0x00); /* 清除内容区域(保留边框) */
}
/**
* @brief 显示可编程消息提示框(支持静态和动态模式)
* @param x 起始列坐标X坐标
* @param y 起始行坐标Y坐标
* @param size 方框大小参数(影响方框尺寸)
* @note 根据编译选项显示静态或动态消息框
* 静态模式:直接绘制完整方框
* 动态模式:逐步绘制方框,产生动画效果
* 方框尺寸:宽度=size高度=size*3/2+9
* @retval 无
*/
void ProgramableMessageBox(uint8_t x, uint8_t y, uint8_t size)
{
#ifdef STATIC_BOX /* 静态框模式 */
FillBoxScreen(x - 1, y - 3, size + 2, size * 3 / 2 + 9, 0x00); /* 清除背景 */
DrawBox(x, y, size, size * 3 / 2 + 9); /* 绘制方框 */
#endif //#ifdef STATIC_BOX
#ifdef DYNAMIC_BOX /* 动态框模式 */
FillBoxScreen(x - 1, y - 3, size * 2 + 2, size * 3 + 9, 0x00); /* 清除背景 */
/* 逐步绘制方框,产生动画效果 */
for(int i = 0; i < size; i++)
{
EraseLine(x, y, i * 2, i * 3); /* 擦除旧线条 */
DrawBox(x, y, (i + 1) * 2, (i + 1) * 3); /* 绘制新方框 */
delay_ms(5); /* 延时,产生动画效果 */
}
#endif //#ifdef DYNAMIC_BOX
}
/**
* @brief 绘制下拉菜单组合框
* @param x 起始列坐标X坐标
* @param y 起始行坐标Y坐标
* @param len 菜单框宽度(像素单位)
* @param items 菜单项数量
* @note 根据编译选项显示静态或动态菜单框
* 静态模式:直接绘制完整菜单框
* 动态模式:逐步绘制菜单框,产生下拉动画效果
* 每个菜单项高度20像素
* @retval 无
*/
void DrawMenuComboBox(uint8_t x, uint8_t y, uint8_t len, uint8_t items)
{
#ifdef STATIC_BOX /* 静态框模式 */
FillBoxScreen(x - 1, y - 6, len + 3, items * 20 + 9, 0x00); /* 清除背景 */
DrawBox(x - 1, y - 3, len + 2, items * 20); /* 绘制菜单框 */
#endif //#ifdef STATIC_BOX
#ifdef DYNAMIC_BOX /* 动态框模式 */
FillBoxScreen(x - 2, y - 6, len + 3, items * 20 + 9, 0x00); /* 清除背景 */
/* 逐步绘制菜单框,产生下拉动画效果 */
for(int i = 0; i < items; i++)
{
EraseLine(x - 1, y - 6, len + 2, i * 20 + 3); /* 擦除旧线条 */
DrawBox(x - 1, y - 3, len + 2, (i + 1) * 20); /* 绘制新菜单框 */
delay_ms(15); /* 延时,产生动画效果 */
}
#endif //#ifdef DYNAMIC_BOX
}
/**
* @brief 显示消息设置提示框(带标题栏,清除背景)
* @param title 标题字符串指针GB2312编码
* @note 显示一个带标题栏的消息设置框
* 先清除背景区域,然后绘制无阴影方框
* 标题栏区域填充白色,标题文字反显
* 位置:坐标(8,40)尺寸37x70像素
* @retval 无
*/
void MessageSetBox(const uint8_t *title)
{
FillBoxScreen(7, 37, 39, 79, 0x00); /* 清除背景区域 */
DrawBox_NoShadow(8, 40, 37, 70); /* 绘制无阴影方框 */
FillBoxScreen(8, 40, 37, 20, 0xFF); /* 填充标题栏区域(白色) */
HZ12AndChar_Printf(18, 43, title, SET); /* 显示标题(反显) */
}
/**
* @brief 显示消息设置提示框(带标题栏,不清除背景)
* @param title 标题字符串指针GB2312编码
* @note 功能与MessageSetBox类似但不清除背景区域
* 用于在已有内容上叠加显示消息框
* @retval 无
*/
void MessageSetBox_NoClear(const uint8_t *title)
{
DrawBox_NoShadow(8, 40, 37, 70); /* 绘制无阴影方框 */
FillBoxScreen(8, 40, 37, 20, 0xFF); /* 填充标题栏区域(白色) */
HZ12AndChar_Printf(18, 43, title, SET); /* 显示标题(反显) */
}
/**
* @brief 清除消息设置框的内容显示区域
* @note 只清除消息框内部的内容区域,保留标题栏和边框
* 用于刷新消息内容而不重新绘制整个框
* @retval 无
*/
void ClrMessageSetBox(void)
{
FillBoxScreen(9, 61, 34, 48, 0x00); /* 清除内容区域(保留标题栏和边框) */
}
/**
* @brief 删除消息设置提示框
* @note 清除整个消息设置框的显示区域
* 包括标题栏、边框和内容区域
* @retval 无
*/
void DeleteMessageSetBox(void)
{
FillBoxScreen(7, 37, 39, 79, 0x00); /* 清除整个消息框区域 */
}
/**
* @brief 绘制记录框(用于显示记录列表)
* @param x 起始列坐标X坐标
* @param y 起始行坐标Y坐标
* @param len 框宽度(像素单位)
* @param items 记录项数量
* @note 绘制一个带阴影的记录列表框
* 每个记录项高度16像素显示区域19像素包括间距
* 先清除背景,然后绘制方框
* @retval 无
*/
void DrawRecordBox(uint8_t x, uint8_t y, uint8_t len, uint8_t items)
{
FillBoxScreen(x - 1, y - 3, len + 2, items * 16 + 6, 0x00); /* 清除背景 */
DrawBox(x - 1, y - 3, len + 2, items * 19); /* 绘制记录框 */
}

View File

@@ -1,117 +1,116 @@
/******************************************************************************
* @file 160160D.h
* @brief UC1698U控制器驱动的 160x160 像素 LCD 显示屏驱动头文件
* @details 本文件定义了 LCD 驱动的接口函数、状态码宏定义以及 ASCII 字体数据。
* 包含显示控制、字符显示、图形显示、菜单操作等功能的函数声明。
* @author 阜阳师范大学物电学院
* @version V0.1
* @date 2026.1.19
* @note 控制器: UC1698U
* 显示屏: 160x160像素
* 颜色模式: 4K色RGB444和 64K色RGB565
******************************************************************************/
#ifndef __160160D_H__
#define __160160D_H__
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"
#define LCD_EXT extern
/* ============================================================================
* 显示状态码定义
* ============================================================================ */
#define DISPLAY_BLANK 0x00 /**< 显示空白 */
#define PROTEC_ON 0x01 /**< 保护开启 */
#define PROTEC_EXIT 0x02 /**< 保护退出 */
#define PROTEC_START 0x03 /**< 保护启动 */
#define PROTECT_TRIP 0x04 /**< 保护跳闸 */
#define HEART_ACCUM 0x05 /**< 心跳累加 */
#define HEART_REDUCE 0x06 /**< 心跳减少 */
#define NEXT_MENU 0x07 /**< 下一菜单 */
#define UP_DOWN 0x08 /**< 上下键 */
#define LEFT_RIGHT 0x09 /**< 左右键 */
#define DERECTION_KEY 0x0a /**< 方向键 */
#define ENTER_KEY 0x0b /**< 确认键 */
#define ESC_KEY 0x0c /**< 取消键 */
#define ADD_DEC 0x0d /**< 加减键 */
#define DI_CLOSE 0x0e /**< 数字输入关闭 */
#define DI_OPEN 0x0f /**< 数字输入开启 */
#define DI_UNCERT 0x10 /**< 数字输入不确定 */
#define DELET_MESS 0x11 /**< 删除消息 */
#define HAVE_MESS 0x12 /**< 有消息 */
#define CODE_PASS 0x13 /**< 密码通过 */
#define CODE_ERROR 0x14 /**< 密码错误 */
/* ============================================================================
* 函数声明(仅包含 160160D.C 中实际定义的函数)
* ============================================================================ */
#define DISPLAY_BLANK 0x00
#define PROTEC_ON 0x01
#define PROTEC_EXIT 0x02
#define PROTEC_START 0x03
#define PROTECT_TRIP 0x04
#define HEART_ACCUM 0x05
#define HEART_REDUCE 0x06
#define NEXT_MENU 0x07
#define UP_DOWN 0x08
#define LEFT_RIGHT 0x09
#define DERECTION_KEY 0x0a
#define ENTER_KEY 0x0b
#define ESC_KEY 0x0c
#define ADD_DEC 0x0d
#define DI_CLOSE 0x0e
#define DI_OPEN 0x0f
#define DI_UNCERT 0x10
#define DELET_MESS 0x11
#define HAVE_MESS 0x12
#define CODE_PASS 0x13
#define CODE_ERROR 0x14
LCD_EXT void display_datas(unsigned char datas); //R-G-B=4-4-4
LCD_EXT void display_line(unsigned char datas);
LCD_EXT void ReverseShow88(uint8_t column ,uint8_t lin,uint8_t const *address);
LCD_EXT void ReverseShow916(uint8_t column ,uint8_t lin,uint8_t const *address);
LCD_EXT void DisplayOneByteS(uint8_t ox, uint8_t oy, uint8_t byte1);
LCD_EXT void DisplayOneText(uint8_t ox,uint8_t oy,uint8_t ascii_code);
LCD_EXT void Clear_Line(uint8_t ox1,uint8_t ox2,uint8_t oy,uint8_t number1,uint8_t number2,uint8_t language);
LCD_EXT void DisplayBrokenLine(uint8_t ox,uint8_t oy,uint8_t num);
LCD_EXT void DisplaySolidLine(uint8_t ox,uint8_t oy,uint8_t num);
LCD_EXT void DisplayLineText2(uint8_t ox,uint8_t oy,uint8_t number,uint8_t const *ptr);
LCD_EXT void DisplayOneChinesetest(uint8_t column,uint8_t lin);
LCD_EXT void REDisplayOneChinesetest(uint8_t column,uint8_t lin);
LCD_EXT void ReverseDispOne(uint8_t ox,uint8_t oy,uint8_t ascii_code);
LCD_EXT void ReverseLineText(uint8_t ox,uint8_t oy,uint8_t number,uint8_t const *ptr);
LCD_EXT void ReverseDispLine(uint8_t ox,uint8_t oy,uint8_t number,uint8_t const *ptr);
LCD_EXT void ReverseOneGraphics(uint8_t column,uint8_t lin,uint16_t hzcode,uint8_t tb_index);
LCD_EXT void display_pic(unsigned char *p);
LCD_EXT void display_pic1(unsigned char *p);
LCD_EXT void DisplayOneGraphics(uint8_t column,uint8_t lin, uint16_t hzcode,uint8_t tb_index);
LCD_EXT void DisplayConnectGraphics(uint8_t column,uint8_t lin,uint16_t hzcode);
LCD_EXT void DisplayOneChinese(uint8_t column,uint8_t lin,uint8_t const *HzCode);
LCD_EXT void DisplayLineChinese(uint8_t ox1,uint8_t oy1,uint8_t number1,uint8_t const *ptr1,uint8_t ox2,uint8_t oy2,uint8_t number2,uint8_t const *ptr2,uint8_t language);
LCD_EXT void DeleteMenuCursor(uint8_t x1,uint8_t y1,uint8_t const *ptr1,uint8_t x2,uint8_t y2,uint8_t const *ptr2,uint8_t num,uint8_t num2,uint8_t language);
LCD_EXT void DisplayMenuCursor(uint8_t x1,uint8_t y1,uint8_t const *ptr1,uint8_t x2,uint8_t y2,uint8_t const *ptr2,uint8_t num,uint8_t num2,uint8_t language);
LCD_EXT void ClearScreen(void);
void ReverseScreen(void);
LCD_EXT void CloseDataCursor(void);
LCD_EXT void DisplayDataCursor2(void);
LCD_EXT void DisplayDataCursor(void);
LCD_EXT void DisplayOneInt(uint8_t ox,uint8_t oy, uint16_t int_value);
LCD_EXT void DisplayOneText2(uint8_t ox,uint8_t oy,uint8_t ascii_code);
LCD_EXT void DisplayHoriLine(uint8_t column,uint8_t lin,uint8_t num);
LCD_EXT void DisplayOneByte(uint8_t ox, uint8_t oy, uint8_t byte1);
LCD_EXT void DisplayGraphicsScreen(void);
LCD_EXT void DisplayLineText(uint8_t ox,uint8_t oy,uint8_t number,uint8_t const *ptr);
LCD_EXT void DisplayOneInt2(uint8_t ox,uint8_t oy, uint16_t int_value);
LCD_EXT void DisplayLongInt(uint8_t ox,uint8_t oy, uint32_t value,uint8_t redixs_point);
LCD_EXT void DisplayOneByte2(uint8_t ox, uint8_t oy, uint8_t byte1);
LCD_EXT void LcdInit(void);
LCD_EXT void ReverseDispNum2(uint8_t ox,uint8_t oy,uint8_t ascii_code);
LCD_EXT void DisplayCount(uint8_t ox, uint8_t oy, uint8_t byte1);
LCD_EXT void DisplayHex(uint8_t ox,uint8_t oy,uint16_t number);
LCD_EXT void ReverseDispOne2(uint8_t ox,uint8_t oy,uint8_t ascii_code);
void Fault_Disp(void);
void KeyRun_Disp(uint32_t Flag);
void IP_Sprintf(uint8_t *buf, uint32_t IPdata);
void IP_Printf(uint8_t x, uint32_t y, uint32_t IPdata, FlagStatus SetFlag, uint32_t cursor);
void HZ12AndChar_Printf(uint8_t x, uint8_t y, const uint8_t *ptr, FlagStatus Flag);
void HZ12AndChar_SignPrintf(uint8_t x, uint8_t y, const uint8_t *ptr, uint32_t SignNUM );
void DisplayNL_LOGO(void);
void DisplayNANRUI_LOGO(void);
void DisplayNANRUI_BMP(void);
void Display_BMP(uint32_t Width, uint32_t Height, const uint8_t* picture);
void DisplayQQ_BMP(void);
void ASCII_Printf(uint8_t x, uint32_t y, uint32_t data, FlagStatus Flag);
void ASCII_SignPrintf(uint8_t x, uint32_t y, uint32_t data, uint32_t SignNUM);
void IntValue_Printf(uint8_t x, uint32_t y, int32_t data, FlagStatus Flag);
void FloatValue_Printf(uint8_t x, uint32_t y, float data, FlagStatus Flag);
void FixLenIToF_Printf(uint32_t x, uint32_t y, int32_t data, uint32_t len, uint32_t dot, FlagStatus Flag, uint32_t cursor);
void FixLenIToF_Sprintf(uint8_t* str, int32_t data, uint32_t len, uint32_t dot);
void DisplayHorizontalLine(uint8_t x,uint8_t y,uint8_t len);
void DisplayVerticalLine(uint8_t x,uint8_t y,uint8_t high, uint8_t value);
void ClearMenuScreen(void);
void ScreenPrintf(uint8_t* ptr);
void Char6_Write(uint8_t x,uint8_t y, uint8_t CharCode, FlagStatus Flag);
void MessageBox(void);
void ClrMessageBox(void);
void ClrMessageBoxContent(void);
void MeunItem_Printf(uint8_t x, uint32_t y, int32_t data, FlagStatus Flag);
void MessageSetBox(const uint8_t *title);
void MessageSetBox_NoClear(const uint8_t *title);
void ClrMessageSetBox(void);
void DeleteMessageSetBox(void);
void ProgramableMessageBox(uint8_t x, uint8_t y, uint8_t size);
void DrawMenuComboBox(uint8_t x, uint8_t y, uint8_t len, uint8_t items);
void DrawRecordBox(uint8_t x, uint8_t y, uint8_t len, uint8_t items);
/* ---------- 硬件初始化函数 ---------- */
void LCD_GPIO_Init(void);
void LCD_Reset(void);
void LCD_InitXRD(void);
void LcdInit(void);
void BackLight_Close(void);
void BackLight_ON(void);
/* ---------- 屏幕控制函数 ---------- */
void ClearScreen(void);
void ReverseScreen(void);
void ClearMenuScreen(void);
void DisplayGraphicsScreen(void);
/* ---------- 字符显示函数 ---------- */
void Char6_Write(uint8_t x, uint8_t y, uint8_t CharCode, FlagStatus Flag);
void Char8_Write(uint8_t x, uint8_t y, uint8_t CharCode, FlagStatus Flag);
void Char12_Write(uint8_t x, uint8_t y, uint8_t CharCode, FlagStatus Flag);
void HZ12_Write(uint8_t x, uint8_t y, const uint8_t *HzCode, FlagStatus Flag);
void HZ12AndChar_Printf(uint8_t x, uint8_t y, const uint8_t *ptr, FlagStatus Flag);
void HZ12AndChar_SignPrintf(uint8_t x, uint8_t y, const uint8_t *ptr, uint32_t SignNUM);
void ASCII_Printf(uint8_t x, uint32_t y, uint32_t data, FlagStatus Flag);
void ASCII_SignPrintf(uint8_t x, uint32_t y, uint32_t data, uint32_t SignNUM);
void ScreenPrintf(uint8_t* ptr);
/* ---------- 数值显示函数 ---------- */
void IntValue_Printf(uint8_t x, uint32_t y, int32_t data, FlagStatus Flag);
void FloatValue_Printf(uint8_t x, uint32_t y, float data, FlagStatus Flag);
void FixLenIntValue_Printf(uint32_t x, uint32_t y, int32_t data, uint32_t len, FlagStatus Flag);
void FixLenIToF_Sprintf(uint8_t* str, int32_t data, uint32_t len, uint32_t dot);
void FixLenIToF_Printf(uint32_t x, uint32_t y, int32_t data, uint32_t len, uint32_t dot, FlagStatus Flag, uint32_t cursor);
void FixLenFloatValue_Printf(uint32_t x, uint32_t y, float data, uint32_t len, uint32_t dot, FlagStatus Flag);
void MeunItem_Printf(uint8_t x, uint32_t y, int32_t data, FlagStatus Flag);
/* ---------- IP 地址显示函数 ---------- */
void IP_Sprintf(uint8_t *buf, uint32_t IPdata);
void IP_Printf(uint8_t x, uint32_t y, uint32_t IPdata, FlagStatus SetFlag, uint32_t cursor);
/* ---------- 图形显示函数 ---------- */
void DisplayOneGraphics(uint8_t column, uint8_t lin, uint16_t hzcode, uint8_t tb_index);
void DisplayNL_LOGO(void);
void DisplayNANRUI_LOGO(void);
void Display_BMP(uint32_t Width, uint32_t Height, const uint8_t* picture);
void DisplayNANRUI_BMP(void);
void DisplayQQ_BMP(void);
void DiaplayHeadGraph(void);
void DrawPoint(uint8_t x, uint8_t y);
/* ---------- 线条绘制函数 ---------- */
void DisplayHorizontalLine(uint8_t x, uint8_t y, uint8_t len);
void DisplayVerticalLine(uint8_t x, uint8_t y, uint8_t high, uint8_t value);
/* ---------- 状态显示函数 ---------- */
void Fault_Disp(void);
void KeyRun_Disp(uint32_t Flag);
/* ============================================================================
* ASCII 字体数据
* ============================================================================ */
/**
* @brief 6x12 点阵 ASCII 字体数据
* @note 包含 95 个 ASCII 字符(空格到波浪号),每个字符 6 列 x 12 行 = 72 位 = 9 字节
* 字符索引0=' ', 1='!', ..., 94='~'
*/
static const uint8_t ASCII6x12[] =
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,/*" ",0*/
@@ -305,6 +304,11 @@ static const uint8_t ASCII6x12[] =
0x40,0xA4,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,/*"~",94*/
};
/**
* @brief 8x12 点阵 ASCII 字体数据
* @note 包含 95 个 ASCII 字符(空格到波浪号),每个字符 8 列 x 12 行 = 96 位 = 12 字节
* 字符索引0=' ', 1='!', ..., 94='~'
*/
static const uint8_t ASCII8x12[] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,/*" ",0*/
@@ -497,6 +501,11 @@ static const uint8_t ASCII8x12[] = {
0x30,0x4C,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,/*"~",94*/
};
/**
* @brief 12x12 点阵 ASCII 字体数据
* @note 包含 95 个 ASCII 字符(空格到波浪号),每个字符 12 列 x 12 行 = 144 位 = 18 字节
* 字符索引0=' ', 1='!', ..., 94='~'
*/
static const uint8_t ASCII12x12[] =
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,57 +0,0 @@
/****************************************************************************************
* 文件名FONT5_7.H
* 功能5*7 ASCII码字体显示函数。(头文件)
* 作者:黄绍斌
* 日期2004.02.26
* 备注使用GUI_SetColor()函数设置前景颜色及背景色。
****************************************************************************************/
#ifndef FONT5_7_H
#define FONT5_7_H
/****************************************************************************
* 名称GUI_PutChar()
* 功能显示ASCII码显示值为20H-7FH(若为其它值,则显示' ')。
* 入口参数: x 指定显示位置x坐标
* y 指定显示位置y坐标
* ch 要显示的ASCII码值。
* 出口参数返回值为1时表示操作成功为0时表示操作失败。
* 说明:操作失败原因是指定地址超出有效范围。
****************************************************************************/
extern unsigned char GUI_PutChar(unsigned int x, unsigned int y, unsigned char ch);
/****************************************************************************
* 名称GUI_PutString()
* 功能:输出显示字符串(没有自动换行功能)。
* 入口参数: x 指定显示位置x坐标
* y 指定显示位置y坐标
* str 要显示的ASCII码字符串
* 出口参数:无
* 说明:操作失败原因是指定地址超出有效范围。
****************************************************************************/
extern void GUI_PutString(unsigned int x, unsigned int y, char *str);
/****************************************************************************
* 名称GUI_PutNoStr()
* 功能:输出显示字符串(没有自动换行功能),若显示的字符个数大于指定个数,则直接退出。
* 入口参数: x 指定显示位置x坐标
* y 指定显示位置y坐标
* str 要显示的ASCII码字符串。
* no 最大显示字符的个数
* 出口参数:无
* 说明:操作失败原因是指定地址超出有效范围。
****************************************************************************/
extern void GUI_PutNoStr(unsigned int x, unsigned int y, char *str, unsigned char no);
/****************************************************************************
* 名称GUI_PutHex()
* 功能显示HEX码显示值为00H-FFH(若为其它值,则显示' ')。
* 入口参数: x 指定显示位置x坐标
* y 指定显示位置y坐标
* v 要显示的HEX。
*出口参数:无
****************************************************************************/
extern void GUI_PutHex(unsigned char x, unsigned char y,unsigned char v);
#endif

View File

@@ -1,63 +1,82 @@
/**
******************************************************************************
* @文件 key.c
* @作者 阜阳师范大学物电学院
* @版本 V0.1
* @日期 2026-01-15
* @简介 按键驱动 - 基于 MultiButton 的高可移植性实现
* @说明
*
****
*/
/******************************************************************************
* @file key.c
* @brief 按键驱动模块 - 基于 MultiButton 库的高可移植性实现
* @details 本文件实现了基于 MultiButton 库的按键驱动功能,支持多按键配置、
* 按键状态检测、按键事件回调等功能。采用配置表方式管理按键,
* 便于扩展和维护。支持单次点击事件检测。
* @author 阜阳师范大学物电学院
* @version V0.1
* @date 2026.1.19
* @note 依赖库: MultiButton
* 支持按键: ENTER, UP, DOWN, LEFT, RIGHT, ESC, ADD, DEC, RESET
* 按键模式: 输入模式,无上拉/下拉
* 特殊处理: PB3 需要禁用 JTAG 才能作为普通 GPIO 使用
******************************************************************************/
#include "key.h"
#include "160160D.h"
// 按键配置结构体
/* ============================================================================
* 按键配置结构体定义
* ============================================================================ */
/**
* @brief 按键配置结构体
* @details 用于存储每个按键的硬件配置信息,包括 GPIO 端口、引脚和按键类型
*/
typedef struct {
uint16_t pin; // GPIO引脚
GPIO_TypeDef* port; // GPIO端口
KEY_TYPE key_type; // 按键ID
uint16_t pin; /**< GPIO 引脚编号(如 GPIO_PIN_12 */
GPIO_TypeDef* port; /**< GPIO 端口(如 GPIOD */
KEY_TYPE key_type; /**< 按键类型标识(如 KEY_ENTER */
} KeyConfig_t;
/*
*******************详细引脚映射*****************************
| 按键名称 | 端口 | 引脚 | 位定义 | 功能说明 |
|---------|------|------|--------|----------|
| KEY_ENTER | GPIOB | PB15 | KEY_ENTER_BIT | 确认键 |
| KEY_UP | GPIOD | PD11 | KEY_UP_BIT | 上键 |
| KEY_DOWN | GPIOD | PD10 | KET_DOWN_BIT | 下键 |
| KEY_LEFT | GPIOD | PD13 | KET_LEFT_BIT | 左键 |
| KEY_RIGHT | GPIOD | PD8 | KET_RIGHT_BIT | 键 |
| KEY_ESC | GPIOD | PD12 | KET_ESC_BIT | 取消键 |
| KEY_ADD | GPIOD | PD14 | KET_ADD_BIT | 键 |
| KEY_DEC | GPIOD | PD9 | KET_DEC_BIT | 键 |
| KEY_RESET | GPIOD | PD15 | KET_RESET_BIT | 复位键 |
***************************************************************
*/
// 按键配置表
/* ============================================================================
* 按键硬件引脚映射表
* ============================================================================ */
/**
* @brief 按键硬件引脚映射表
* @details 详细引脚映射关系如下:
* | 按键名称 | 端口 | 引脚 | 按键类型 | 功能说明 |
* |---------|------|------|----------|----------|
* | KEY_ENTER | GPIOD | PD12 | KEY_ENTER | 确认键 |
* | KEY_UP | GPIOD | PD11 | KEY_UP | 键 |
* | KEY_DOWN | GPIOD | PD10 | KEY_DOWN | 键 |
* | KEY_LEFT | GPIOD | PD13 | KEY_LEFT | 键 |
* | KEY_RIGHT | GPIOD | PD8 | KEY_RIGHT | 键 |
* | KEY_ESC | GPIOB | PB15 | KEY_ESC | 取消键 |
* | KEY_ADD | GPIOB | PB3 | KEY_ADD | 加键(需禁用 JTAG |
* | KEY_RESET | GPIOD | PD15 | KEY_RESET | 复位键 |
* @note PB3 引脚默认被 JTAG 占用,需要禁用 JTAG 才能作为普通 GPIO 使用
*/
static const KeyConfig_t key_configs[] = {
{GPIO_PIN_12, GPIOD, KEY_ENTER},
{GPIO_PIN_11, GPIOD, KEY_UP},
{GPIO_PIN_10, GPIOD, KEY_DOWN},
{GPIO_PIN_13, GPIOD, KEY_LEFT},
{GPIO_PIN_8, GPIOD, KEY_RIGHT},
{GPIO_PIN_15, GPIOB, KEY_ESC},
{GPIO_PIN_3, GPIOB, KEY_ADD},
{GPIO_PIN_15, GPIOD, KEY_RESET},
{GPIO_PIN_12, GPIOD, KEY_ENTER}, /**< 确认键PD12 */
{GPIO_PIN_11, GPIOD, KEY_UP}, /**< 上键PD11 */
{GPIO_PIN_10, GPIOD, KEY_DOWN}, /**< 下键PD10 */
{GPIO_PIN_13, GPIOD, KEY_LEFT}, /**< 左键PD13 */
{GPIO_PIN_8, GPIOD, KEY_RIGHT}, /**< 右键PD8 */
{GPIO_PIN_15, GPIOB, KEY_ESC}, /**< 取消键PB15 */
{GPIO_PIN_3, GPIOB, KEY_ADD}, /**< 加键PB3需禁用 JTAG */
{GPIO_PIN_15, GPIOD, KEY_RESET}, /**< 复位键PD15 */
};
#define KEY_COUNT (sizeof(key_configs) / sizeof(key_configs[0]))
// 按键句柄数组
static Button btn_handles[KEY_COUNT];
// 业务逻辑回调函数指针由main.c注册
static KeyCallback key_callback = NULL;
#define KEY_COUNT (sizeof(key_configs) / sizeof(key_configs[0])) /**< 按键总数 */
/* ============================================================================
* 全局变量定义
* ============================================================================ */
static Button btn_handles[KEY_COUNT]; /**< MultiButton 按键句柄数组 */
static KeyCallback key_callback = NULL; /**< 业务逻辑层注册的回调函数指针 */
/* ============================================================================
* 内部函数实现
* ============================================================================ */
/**
* @brief 读取按键GPIO电平
* @param button_id 按键ID
* @retval 0:按下, 1:释放
* @brief 读取按键 GPIO 电平状态
* @param button_id 按键 ID(对应 key_configs 数组索引0 到 KEY_COUNT-1
* @note 该函数由 MultiButton 库调用,用于读取按键的硬件状态
* 返回值0 表示按下低电平1 表示释放(高电平)
* @retval 0: 按键按下(低电平)
* @retval 1: 按键释放(高电平)
*/
static uint8_t button_read_level(uint8_t button_id)
{
@@ -66,82 +85,117 @@ static uint8_t button_read_level(uint8_t button_id)
}
/**
* @brief 统一的按键回调函数(内部使用,调用业务逻辑回调
* @param btn 按键句柄指针
* @brief 统一的按键事件回调函数(内部使用)
* @param btn MultiButton 库的按键句柄指针
* @note 当 MultiButton 库检测到按键事件时,会调用此函数
* 此函数将按键事件转换为业务逻辑层的按键类型,并调用注册的回调函数
* 处理流程:
* 1. 检查按键句柄和回调函数是否有效
* 2. 从按键句柄中获取 button_id
* 3. 通过 button_id 查找对应的按键类型
* 4. 调用业务逻辑层注册的回调函数
* @retval 无
*/
static void button_callback(Button* btn)
{
if (btn != NULL && key_callback != NULL)
{
// 通过button_id获取对应的按键类型
/* 通过 button_id 获取对应的按键类型 */
uint8_t button_id = btn->button_id;
if (button_id < KEY_COUNT)
{
// 调用业务逻辑层注册的回调函数
/* 调用业务逻辑层注册的回调函数 */
key_callback(key_configs[button_id].key_type);
}
}
}
/**
* @brief 注册按键回调函数(供业务逻辑层调用)
* @param callback 回调函数指针
* @brief 使能指定 GPIO 端口的时钟
* @param port GPIO 端口指针(如 GPIOA, GPIOB, GPIOD 等)
* @note 使用 switch-case 结构实现,代码简洁清晰
* 支持所有 GPIO 端口A-G
* 在初始化 GPIO 之前必须使能对应的时钟
* @retval 无
*/
static void KEY_GPIO_ClockEnable(GPIO_TypeDef *port)
{
switch ((uint32_t)port) {
case (uint32_t)GPIOA: __HAL_RCC_GPIOA_CLK_ENABLE(); break; /**< 使能 GPIOA 时钟 */
case (uint32_t)GPIOB: __HAL_RCC_GPIOB_CLK_ENABLE(); break; /**< 使能 GPIOB 时钟 */
case (uint32_t)GPIOC: __HAL_RCC_GPIOC_CLK_ENABLE(); break; /**< 使能 GPIOC 时钟 */
case (uint32_t)GPIOD: __HAL_RCC_GPIOD_CLK_ENABLE(); break; /**< 使能 GPIOD 时钟 */
case (uint32_t)GPIOE: __HAL_RCC_GPIOE_CLK_ENABLE(); break; /**< 使能 GPIOE 时钟 */
case (uint32_t)GPIOF: __HAL_RCC_GPIOF_CLK_ENABLE(); break; /**< 使能 GPIOF 时钟 */
case (uint32_t)GPIOG: __HAL_RCC_GPIOG_CLK_ENABLE(); break; /**< 使能 GPIOG 时钟 */
default: break;
}
}
/* ============================================================================
* 外部接口函数实现
* ============================================================================ */
/**
* @brief 按键硬件初始化函数
* @note 初始化流程:
* 1. 使能 AFIO 时钟(修改 AFIO 寄存器前必须)
* 2. 禁用 JTAG保留 SWD释放 PA15、PB3、PB4 作为普通 GPIO
* 3. 遍历按键配置表,对每个按键进行初始化:
* a. 使能对应 GPIO 端口时钟
* b. 配置 GPIO 为输入模式,无上拉/下拉,高速模式
* c. 初始化 MultiButton 按键句柄
* d. 绑定单次点击事件到回调函数
* e. 启动按键检测
* @note 特殊处理:由于使用了 PB3 引脚,需要禁用 JTAG 功能
* JTAG 禁用后PA13、PA14 仍可用于 SWD 调试
* @retval 无
*/
void Key_Init(void)
{
/* ========== AFIO 和 JTAG 配置 ========== */
__HAL_RCC_AFIO_CLK_ENABLE(); /**< 使能 AFIO 时钟(修改 AFIO 寄存器前必须) */
/* 禁用 JTAG保留 SWDPA13、PA14 仍可用于调试) */
/* 这会释放 PA15、PB3、PB4 作为普通 GPIO */
__HAL_AFIO_REMAP_SWJ_NOJTAG();
/* ========== GPIO 和按键初始化 ========== */
GPIO_InitTypeDef gpio_init_struct = {0};
/* 批量初始化所有按键 */
for (uint8_t button_id = 0; button_id < KEY_COUNT; button_id++)
{
/* 使能对应 GPIO 端口时钟 */
KEY_GPIO_ClockEnable(key_configs[button_id].port);
/* 配置 GPIO 为输入模式 */
gpio_init_struct.Pin = key_configs[button_id].pin;
gpio_init_struct.Mode = GPIO_MODE_INPUT; /**< 输入模式 */
gpio_init_struct.Pull = GPIO_NOPULL; /**< 无上拉/下拉(外部电路处理) */
gpio_init_struct.Speed = GPIO_SPEED_HIGH; /**< 高速模式,提高响应速度 */
HAL_GPIO_Init(key_configs[button_id].port, &gpio_init_struct);
/* 初始化 MultiButton 按键句柄 */
button_init(&btn_handles[button_id], button_read_level, 0, button_id);
/* 绑定单次点击事件到回调函数 */
button_attach(&btn_handles[button_id], BTN_SINGLE_CLICK, button_callback);
/* 启动按键检测 */
button_start(&btn_handles[button_id]);
}
}
/**
* @brief 注册按键回调函数(供业务逻辑层调用)
* @param callback 按键回调函数指针,当检测到按键事件时会调用此函数
* @note 业务逻辑层通过此函数注册按键处理回调
* 回调函数参数为 KEY_TYPE 类型,表示被按下的按键类型
* 如果传入 NULL则取消已注册的回调函数
* @retval 无
*/
void Key_RegisterCallback(KeyCallback callback)
{
key_callback = callback;
}
/**
* @brief 使能GPIO时钟
* @param port: GPIO端口
* @retval 无
* @note 使用switch-case结构代码更简洁清晰支持所有GPIO端口A-G
*/
static void KEY_GPIO_ClockEnable(GPIO_TypeDef *port)
{
switch ((uint32_t)port) {
case (uint32_t)GPIOA: __HAL_RCC_GPIOA_CLK_ENABLE(); break;
case (uint32_t)GPIOB: __HAL_RCC_GPIOB_CLK_ENABLE(); break;
case (uint32_t)GPIOC: __HAL_RCC_GPIOC_CLK_ENABLE(); break;
case (uint32_t)GPIOD: __HAL_RCC_GPIOD_CLK_ENABLE(); break;
case (uint32_t)GPIOE: __HAL_RCC_GPIOE_CLK_ENABLE(); break;
case (uint32_t)GPIOF: __HAL_RCC_GPIOF_CLK_ENABLE(); break;
case (uint32_t)GPIOG: __HAL_RCC_GPIOG_CLK_ENABLE(); break;
default: break;
}
}
/**************************************************************************************
* FunctionName : Key_Init()
* Description : 按键硬件初始化使用MultiButton库
* EntryParameter : none
* ReturnValue : none
**************************************************************************************/
void Key_Init(void)
{
/*由于使用了 PB3 才需要特殊加的*/
__HAL_RCC_AFIO_CLK_ENABLE(); // AFIO时钟修改AFIO寄存器前必须
/* 禁用JTAG保留SWDPA13、PA14仍可用于调试*/
/* 这会释放 PA15、PB3、PB4 作为普通GPIO */
__HAL_AFIO_REMAP_SWJ_NOJTAG();
GPIO_InitTypeDef gpio_init_struct = {0};
// 批量初始化GPIO
for (uint8_t button_id = 0; button_id < KEY_COUNT; button_id++)
{
/* 使能对应GPIO时钟 */
KEY_GPIO_ClockEnable(key_configs[button_id].port);
// 配置并初始化引脚
gpio_init_struct.Pin = key_configs[button_id].pin;
gpio_init_struct.Mode = GPIO_MODE_INPUT;
gpio_init_struct.Pull = GPIO_NOPULL;
gpio_init_struct.Speed = GPIO_SPEED_HIGH;
HAL_GPIO_Init(key_configs[button_id].port, &gpio_init_struct);
button_init(&btn_handles[button_id], button_read_level, 0, button_id);
button_attach(&btn_handles[button_id], BTN_SINGLE_CLICK, button_callback);
button_start(&btn_handles[button_id]);
}
}

View File

@@ -1,35 +1,86 @@
/******************************************************************************
* @file key.h
* @brief 按键驱动模块头文件
* @details 本文件定义了按键驱动的接口和数据结构,包括按键类型枚举、
* 按键回调函数类型定义以及外部接口函数声明。
* @author 阜阳师范大学物电学院
* @version V0.1
* @date 2026.1.19
* @note 依赖库: MultiButton
* 使用方式:
* 1. 调用 Key_Init() 初始化按键硬件
* 2. 调用 Key_RegisterCallback() 注册按键回调函数
* 3. 在定时器中调用 Button_Ticks() 进行按键扫描
******************************************************************************/
#ifndef __KEY_H__
#define __KEY_H__
#include "./SYSTEM/sys/sys.h"
#include "MultiButton.h"
/* ============================================================================
* 按键类型枚举定义
* ============================================================================ */
/**
* @brief 按键类型枚举
* @details 定义了系统中所有支持的按键类型
*/
typedef enum
{
KEY_NONE = 0, // 没有按键
KEY_ENTER, // 确认
KEY_UP, // 向上
KEY_DOWN, // 向下
KEY_ESC, //取消
KEY_ADD, //加
KEY_DEC, //减
KEY_LEFT, //左
KEY_RIGHT, //右
KEY_RESET, //复位
KEY_FACTORY, //工厂
KEY_NONE = 0, /**< 无按键按下 */
KEY_ENTER, /**< 确认键 */
KEY_UP, /**< 向上键 */
KEY_DOWN, /**< 向下键 */
KEY_ESC, /**< 取消键 */
KEY_ADD, /**< 加键 */
KEY_DEC, /**< 减键 */
KEY_LEFT, /**< 向左键 */
KEY_RIGHT, /**< 向右键 */
KEY_RESET, /**< 复位键 */
KEY_FACTORY, /**< 工厂模式键 */
} KEY_TYPE;
// 按键回调函数类型定义(业务逻辑层使用)
// 参数: key_type - 按键类型
/* ============================================================================
* 回调函数类型定义
* ============================================================================ */
/**
* @brief 按键回调函数类型定义
* @param key_type 按键类型KEY_TYPE 枚举值)
* @note 业务逻辑层需要实现此类型的回调函数,用于处理按键事件
* 当检测到按键按下时,会调用注册的回调函数
* @retval 无
*/
typedef void (*KeyCallback)(KEY_TYPE key_type);
/* ============================================================================
* 外部接口函数声明
* ============================================================================ */
/**
* @brief 按键硬件初始化函数
* @note 初始化所有按键的 GPIO 配置和 MultiButton 库
* 特殊处理:禁用 JTAG 以释放 PB3 等引脚
* 必须在系统初始化时调用一次
* @retval 无
*/
void Key_Init(void);
// 按键驱动函数在key.c中实现使用MultiButton库
void Key_Init(void); // 按键初始化
void Key_RegisterCallback(KeyCallback callback); // 注册按键回调函数(业务逻辑层调用)
#define Button_Ticks() button_ticks()
#endif
/**
* @brief 注册按键回调函数(供业务逻辑层调用)
* @param callback 按键回调函数指针,当检测到按键事件时会调用此函数
* @note 业务逻辑层通过此函数注册按键处理回调
* 如果传入 NULL则取消已注册的回调函数
* @retval 无
*/
void Key_RegisterCallback(KeyCallback callback);
/**
* @brief MultiButton 库的按键扫描函数宏定义
* @note 需要在定时器中周期性调用此函数(建议 5-10ms 调用一次)
* 用于 MultiButton 库进行按键状态扫描和事件检测
* @retval 无
*/
#define Button_Ticks() button_ticks()
#endif /* __KEY_H__ */

View File

@@ -1,127 +1,134 @@
/******************************************************************************
* @file rs485.c
* @brief rs485串口配置,主要配置两种模式 中断接收模式 和 DMA 接收模式
* @details
* 1. DMA模式是主要工作模式
* 2. 中断模式用于错误恢复和接收第一个短帧
* 3. 中断处理完后自动切换回DMA模式
* 4. 中断接收主要用于异常情况处理
* @brief RS485 串口驱动 - DMA 与中断接收
* @details 本文件实现 RS485 串口通信驱动,支持两种模式:
* 1. DMA 模式:主工作模式,使用 ReceiveToIdle 接收不定长帧
* 2. 中断模式用于错误恢复或短帧接收RXNE 逐字节接收
* 收发通过 DE 引脚PA1切换低电平发送高电平接收。
* @author 阜阳师范大学物电学院
* @version V0.01
* @date 2026.1.24
* @note 通信协议Modbus RTU
* 通信接口RS485
* 主站地址0x01
* @note USART2TX=PA2RX=PA3DE=PA1波特率 700000
******************************************************************************/
#include "rs485.h"
/* ============================================================================
* USART 与引脚宏定义
* ============================================================================ */
#define RS485_UX USART2
#define RS485_UX_IRQn USART2_IRQn
#define RS485_UX_IRQHandler USART2_IRQHandler
#define RS485_UX_CLK_ENABLE() do{ __HAL_RCC_USART2_CLK_ENABLE(); }while(0) /* 时钟使能 */
#define RS485_UX_CLK_ENABLE() do{ __HAL_RCC_USART2_CLK_ENABLE(); }while(0) /**< USART2 时钟使能 */
#define RS485_SEND_ENABLE() do{HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);}while(0)
#define RS485_RECEIVE_ENABLE() do{HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);}while(0)
#define RS485_SEND_ENABLE() do{HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);}while(0) /**< DE 低:发送 */
#define RS485_RECEIVE_ENABLE() do{HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);}while(0) /**< DE 高:接收 */
#define RS485_ENABLE_GPIO_PORT GPIOA
#define RS485_ENABLE_GPIO_PIN GPIO_PIN_1
#define RS485_ENABLE_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */
#define RS485_ENABLE_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)
#define RS485_TX_GPIO_PORT GPIOA
#define RS485_TX_GPIO_PIN GPIO_PIN_2
#define RS485_TX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */
#define RS485_TX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)
#define RS485_RX_GPIO_PORT GPIOA
#define RS485_RX_GPIO_PIN GPIO_PIN_3
#define RS485_RX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */
#define RS485_RX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)
RS485_REGISTER_TYPE RS485REG = {RESET, {0}, {0}}; /* 零初始化定义 */
#define BAUDRATE (700000) /**< 波特率 */
// HAL库UART句柄
UART_HandleTypeDef rs485_handle; /* UART句柄 */
// HAL库DMA句柄接收
DMA_HandleTypeDef hdma_rs485_rx;
// HAL库DMA句柄发送
DMA_HandleTypeDef hdma_rs485_tx;
/* ============================================================================
* 全局变量定义
* ============================================================================ */
RS485_REGISTER_TYPE RS485REG = {RESET, {0}, {0}}; /**< RS485 收发寄存器,零初始化 */
UART_HandleTypeDef rs485_handle; /**< HAL UART 句柄 */
DMA_HandleTypeDef hdma_rs485_rx; /**< HAL DMA 接收句柄DMA1 Channel6 */
DMA_HandleTypeDef hdma_rs485_tx; /**< HAL DMA 发送句柄(未使用,保留) */
#define BAUDRATE (700000)
/* ============================================================================
* 初始化函数
* ============================================================================ */
/**
* @brief 串口X初始化函数
* @param baudrate: 波特率, 根据自己需要设置波特率值
* @note 注意: 必须设置正确的时钟源, 否则串口波特率就会设置异常.
* 这里的USART的时钟源在sys_stm32_clock_init()函数中已经设置过了.
* @retval 无
* @brief RS485 初始化DMA 接收模式)
* @note 配置流程:
* 1. 使能 GPIO、USART 时钟
* 2. 配置 TX(PA2)、RX(PA3)、DE(PA1)
* 3. 配置 DMA1 Channel6 接收,关联 UART
* 4. 使能 DMA、USART 中断
* 5. 初始化 UART8N1无流控波特率 BAUDRATE
* 6. 切换为接收,启动 ReceiveToIdle DMA
* USART 时钟源需在 sys_stm32_clock_init 中已配置。
* @retval 无
*/
void RS485_DMA_init()
void RS485_DMA_init(void)
{
/* 开启时钟 */
RS485_TX_GPIO_CLK_ENABLE(); /* 使能串口TX脚时钟 */
RS485_RX_GPIO_CLK_ENABLE(); /* 使能串口RX脚时钟 */
RS485_ENABLE_GPIO_CLK_ENABLE(); /* 使能串口RX脚时钟 */
RS485_UX_CLK_ENABLE(); /* 使能串口时钟 */
/* 使能时钟 */
RS485_TX_GPIO_CLK_ENABLE();
RS485_RX_GPIO_CLK_ENABLE();
RS485_ENABLE_GPIO_CLK_ENABLE();
RS485_UX_CLK_ENABLE();
/*GPIO 初始化设置*/
/* GPIO 初始化 */
GPIO_InitTypeDef gpio_init_struct = {0};
gpio_init_struct.Pin = RS485_TX_GPIO_PIN; /* 串口发送引脚号 */
gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 复用推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* IO速度设置为高速 */
gpio_init_struct.Pin = RS485_TX_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_AF_PP;
gpio_init_struct.Pull = GPIO_PULLUP;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(RS485_TX_GPIO_PORT, &gpio_init_struct);
gpio_init_struct.Pin = RS485_RX_GPIO_PIN; /* 串口RX脚 模式设置 */
gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;
HAL_GPIO_Init(RS485_RX_GPIO_PORT, &gpio_init_struct); /* 串口RX脚 必须设置成输入模式 */
gpio_init_struct.Pin = RS485_ENABLE_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(RS485_ENABLE_GPIO_PORT, &gpio_init_struct);
gpio_init_struct.Pin = RS485_RX_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;
HAL_GPIO_Init(RS485_RX_GPIO_PORT, &gpio_init_struct);
/* 配置DMA接收句柄 */
gpio_init_struct.Pin = RS485_ENABLE_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(RS485_ENABLE_GPIO_PORT, &gpio_init_struct);
/* DMA 接收配置 */
hdma_rs485_rx.Instance = DMA1_Channel6;
hdma_rs485_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; // 外设到内存
hdma_rs485_rx.Init.PeriphInc = DMA_PINC_DISABLE; // 外设地址不递增
hdma_rs485_rx.Init.MemInc = DMA_MINC_ENABLE; // 内存地址递增
hdma_rs485_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; // 外设数据对齐:字节
hdma_rs485_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; // 内存数据对齐:字节
hdma_rs485_rx.Init.Mode = DMA_NORMAL; // 正常模式
hdma_rs485_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH; // 最高优先级
HAL_DMA_Init(&hdma_rs485_rx); /*初始化DMA 接收*/
hdma_rs485_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_rs485_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_rs485_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_rs485_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_rs485_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_rs485_rx.Init.Mode = DMA_NORMAL;
hdma_rs485_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
HAL_DMA_Init(&hdma_rs485_rx);
/* 关联 DMA 到 UART重要*/
__HAL_LINKDMA(&rs485_handle, hdmarx, hdma_rs485_rx);
/* 使能DMA传输完成中断 */
HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 0, 0);
/* UART 初始化设置 */
rs485_handle.Instance = RS485_UX; /* USART_UX */
rs485_handle.Init.BaudRate = BAUDRATE; /* 波特率 */
rs485_handle.Init.WordLength = UART_WORDLENGTH_8B; /* 字长为8位数据格式 */
rs485_handle.Init.StopBits = UART_STOPBITS_1; /* 一个停止位 */
rs485_handle.Init.Parity = UART_PARITY_NONE; /* 无奇偶校验位 */
rs485_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE; /* 无硬件流控 */
rs485_handle.Init.Mode = UART_MODE_TX_RX; /* 收发模式 */
/* UART 初始化 */
rs485_handle.Instance = RS485_UX;
rs485_handle.Init.BaudRate = BAUDRATE;
rs485_handle.Init.WordLength = UART_WORDLENGTH_8B;
rs485_handle.Init.StopBits = UART_STOPBITS_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;
HAL_UART_Init(&rs485_handle); /* 初始化UART */
HAL_UART_Init(&rs485_handle);
HAL_NVIC_EnableIRQ(RS485_UX_IRQn); /* 使能USART中断通道 */
HAL_NVIC_SetPriority(RS485_UX_IRQn, 1, 0); /* 组2抢占优先级1子优先级0 */
/* 启动DMA接收 */
RS485_RECEIVE_ENABLE();
HAL_NVIC_EnableIRQ(RS485_UX_IRQn);
HAL_NVIC_SetPriority(RS485_UX_IRQn, 1, 0);
/* 启动 DMA 接收 */
RS485_RECEIVE_ENABLE();
HAL_UARTEx_ReceiveToIdle_DMA(&rs485_handle, (uint8_t *)RS485REG.DR, UART_RX_LEN);
}
/**
* @brief 串口X初始化函数
* @note 注意: 必须设置正确的时钟源, 否则串口波特率就会设置异常.
*
* @retval
* @brief RS485 初始化(中断接收模式)
* @note 配置 GPIO、UART使能 RXNE 中断。不启用 DMA。
* 用于错误恢复或接收短帧。USART 时钟源需已配置。
* @retval 无
*/
void RS485_init()
void RS485_init(void)
{
/* 开启时钟 */
RS485_TX_GPIO_CLK_ENABLE(); /* 使能串口TX脚时钟 */
@@ -129,80 +136,94 @@ void RS485_init()
RS485_ENABLE_GPIO_CLK_ENABLE(); /* 使能串口RX脚时钟 */
RS485_UX_CLK_ENABLE(); /* 使能串口时钟 */
/*GPIO 初始化设置*/
GPIO_InitTypeDef gpio_init_struct;
gpio_init_struct.Pin = RS485_TX_GPIO_PIN; /* 串口发送引脚号 */
gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 复用推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* IO速度设置为高速 */
/* GPIO 初始化 */
GPIO_InitTypeDef gpio_init_struct = {0};
gpio_init_struct.Pin = RS485_TX_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_AF_PP;
gpio_init_struct.Pull = GPIO_PULLUP;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(RS485_TX_GPIO_PORT, &gpio_init_struct);
gpio_init_struct.Pin = RS485_RX_GPIO_PIN; /* 串口RX脚 模式设置 */
gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;
HAL_GPIO_Init(RS485_RX_GPIO_PORT, &gpio_init_struct); /* 串口RX脚 必须设置成输入模式 */
gpio_init_struct.Pin = RS485_ENABLE_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(RS485_ENABLE_GPIO_PORT, &gpio_init_struct);
gpio_init_struct.Pin = RS485_RX_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;
HAL_GPIO_Init(RS485_RX_GPIO_PORT, &gpio_init_struct);
gpio_init_struct.Pin = RS485_ENABLE_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(RS485_ENABLE_GPIO_PORT, &gpio_init_struct);
/*UART 初始化设置*/
rs485_handle.Instance = RS485_UX; /* USART_UX */
rs485_handle.Init.BaudRate = BAUDRATE; /* 波特率 */
rs485_handle.Init.WordLength = UART_WORDLENGTH_8B; /* 字长为8位数据格式 */
rs485_handle.Init.StopBits = UART_STOPBITS_1; /* 一个停止位 */
rs485_handle.Init.Parity = UART_PARITY_NONE; /* 无奇偶校验位 */
rs485_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE; /* 无硬件流控 */
rs485_handle.Init.Mode = UART_MODE_TX_RX; /* 收发模式 */
HAL_UART_Init(&rs485_handle); /* 使能UART */
/* UART 初始化 */
rs485_handle.Instance = RS485_UX;
rs485_handle.Init.BaudRate = BAUDRATE;
rs485_handle.Init.WordLength = UART_WORDLENGTH_8B;
rs485_handle.Init.StopBits = UART_STOPBITS_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;
HAL_UART_Init(&rs485_handle);
/* 该函数会开启接收中断标志位UART_IT_RXNE并且设置接收缓冲以及接收缓冲接收最大数据量 */
__HAL_UART_ENABLE_IT(&rs485_handle, UART_IT_RXNE);
HAL_NVIC_EnableIRQ(RS485_UX_IRQn); /* 使能USART中断通道 */
HAL_NVIC_SetPriority(RS485_UX_IRQn, 1, 0); /* 组2抢占优先级1子优先级0 */
__HAL_UART_ENABLE_IT(&rs485_handle, UART_IT_RXNE);
HAL_NVIC_EnableIRQ(RS485_UX_IRQn);
HAL_NVIC_SetPriority(RS485_UX_IRQn, 1, 0);
RS485_RECEIVE_ENABLE();
}
/* ============================================================================
* 回调与中断服务函数
* ============================================================================ */
/**
* @brief UART 接收事件回调ReceiveToIdle 完成时由 HAL 调用)
* @param huart UART 句柄
* @param Size 本帧接收到的字节数
* @note 当为 RS485 所用 UART 时:置位 NewMessageFlag并重新启动 ReceiveToIdle DMA。
* @retval 无
*/
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if(huart->Instance == RS485_UX)
if (huart->Instance == RS485_UX)
{
RS485REG.NewMessageFlag = SET;
HAL_UARTEx_ReceiveToIdle_DMA(&rs485_handle, (uint8_t *)RS485REG.DR, UART_RX_LEN);
}
}
/**
* @brief 485串口中断服务函数
* @param 无
* @retval
* @brief RS485 所用 USART 中断服务函数
* @note 调用 HAL_UART_IRQHandler 处理 UART 及关联 DMA 中断。
* @retval 无
*/
void RS485_UX_IRQHandler(void)
{
HAL_UART_IRQHandler(&rs485_handle);
}
/*******************************************************************************
* FunctionName : DMA1_Channel6_IRQHandler()
* Description : 串口接收中断
* EntryParameter : none
* ReturnValue : none
********************************************************************************/
/**
* @brief DMA1 Channel6 中断服务函数RS485 接收 DMA
* @note 调用 HAL_DMA_IRQHandler在 ReceiveToIdle 完成等事件时触发。
* @retval 无
*/
void DMA1_Channel6_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hdma_rs485_rx);
HAL_DMA_IRQHandler(&hdma_rs485_rx);
}
/***********************************************************************
* FunctionName : RS485_SendBuff()
* Description : 485发送数组
* EntryParameter : *ptr:待发送的字符串, len:待发送的数据个数
* ReturnValue : ptrLen:发送的数据个数
**************************************************************************/
/* ============================================================================
* 发送接口
* ============================================================================ */
/**
* @brief RS485 发送数据
* @param ptr 待发送数据指针
* @param len 待发送字节数
* @note 切换为发送DE 低)→ 阻塞发送 → 切换回接收DE 高)。超时 1000ms。
* @retval 无
*/
void RS485_SendBuff(uint8_t *ptr, uint32_t len)
{
RS485_SEND_ENABLE();
HAL_UART_Transmit(&rs485_handle, ptr,len, 1000);
HAL_UART_Transmit(&rs485_handle, ptr, len, 1000);
RS485_RECEIVE_ENABLE();
}

View File

@@ -1,29 +1,70 @@
/******************************************************************************
* @file rs485.h
* @brief RS485 串口通信驱动头文件
* @details 本文件声明 RS485 通信相关接口与数据结构,支持 DMA 接收与中断接收两种模式。
* 使用 USART2收发切换通过 DE 引脚PA1控制。
* @author 阜阳师范大学物电学院
* @version V0.01
* @date 2026.1.24
* @note USARTUSART2
* TXPA2RXPA3DEPA1
* 波特率700000
******************************************************************************/
#ifndef __RS485_H__
#define __RS485_H__
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"
#define UART_RX_LEN (3208)
#define UART_TX_LEN (8)
/* ============================================================================
* 宏定义
* ============================================================================ */
#define UART_RX_LEN (3208) /**< 接收缓冲区长度(字节),与 Modbus 数据帧长度一致 */
#define UART_TX_LEN (8) /**< 发送缓冲区长度(字节) */
/* ============================================================================
* 数据结构定义
* ============================================================================ */
/**
* @brief RS485 收发寄存器结构体
* @note 用于存放一帧接收数据及发送缓存,与 Modbus 处理模块配合使用
*/
typedef struct
{
volatile FlagStatus NewMessageFlag; //一帧数据接收完整
volatile uint8_t DR[UART_RX_LEN]; //接收缓存
volatile uint8_t TDR[UART_TX_LEN]; //发送缓存
{
volatile FlagStatus NewMessageFlag; /**< 新消息标志:一帧数据接收完成时置位 */
volatile uint8_t DR[UART_RX_LEN]; /**< 接收缓存 */
volatile uint8_t TDR[UART_TX_LEN]; /**< 发送缓存 */
} RS485_REGISTER_TYPE;
extern RS485_REGISTER_TYPE RS485REG;
extern RS485_REGISTER_TYPE RS485REG; /**< 全局 RS485 收发寄存器 */
/* ============================================================================
* 函数声明
* ============================================================================ */
void USART2_Init(void);
/**
* @brief RS485 初始化DMA 接收模式)
* @note 配置 GPIO、UART、DMA启动 ReceiveToIdle DMA 接收。为主工作模式。
* @retval 无
*/
void RS485_DMA_init(void);
/**
* @brief RS485 初始化(中断接收模式)
* @note 配置 GPIO、UART使能 RXNE 中断接收。用于异常恢复或短帧接收。
* @retval 无
*/
void RS485_init(void);
/**
* @brief RS485 发送数据
* @param ptr 待发送数据指针
* @param len 待发送字节数
* @note 先切换为发送DE 高发送完成后切回接收DE 低)。使用阻塞方式发送。
* @retval 无
*/
void RS485_SendBuff(uint8_t *ptr, uint32_t len);
#endif
#endif /* __RS485_H__ */