feat(bluetooth): 添加多按钮支持和WiFi连接音频反馈

添加MultiButton库支持多按键功能,重构SPI显示屏驱动代码,
迁移MP3音频文件至正确目录并集成WiFi连接状态音频提示音。

- 添加Multi_Button.c源文件和相关头文件包含
- 重构spi_st7735s.c中的数组初始化格式,优化代码可读性
- 将MP3音频文件从Development_Docs/MP3迁移到Core/Bsp/BSP_Device/bsp_mp3/MP3
- 在WiFi连接过程中添加MP3音频反馈(连接成功/失败提示音)
- 优化ST7735显示屏驱动中的DMA传输模式支持
```
This commit is contained in:
2026-02-23 16:59:34 +08:00
parent ce8d6fd2eb
commit 9cadad138e
37 changed files with 980 additions and 201 deletions

View File

@@ -43,6 +43,7 @@ add_executable(${CMAKE_PROJECT_NAME}
Core/Bsp/BSP_Device/spi_st7735s/spi_st7735s.c Core/Bsp/BSP_Device/spi_st7735s/spi_st7735s.c
Core/Bsp/BSP_Device/spi_st7735s/fonts.c Core/Bsp/BSP_Device/spi_st7735s/fonts.c
Core/Bsp/BSP_Device/bsp_mp3/mp3_driver.c Core/Bsp/BSP_Device/bsp_mp3/mp3_driver.c
Core/Bsp/MultiButton-master/Multi_Button.c
) )
# Add STM32CubeMX generated sources # Add STM32CubeMX generated sources
add_subdirectory(cmake/stm32cubemx) add_subdirectory(cmake/stm32cubemx)
@@ -68,6 +69,7 @@ target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
Core/Bsp/BSP_Device/device_ctrl Core/Bsp/BSP_Device/device_ctrl
Core/Bsp/BSP_Device/spi_st7735s Core/Bsp/BSP_Device/spi_st7735s
Core/Bsp/BSP_Device/bsp_mp3 Core/Bsp/BSP_Device/bsp_mp3
Core/Bsp/MultiButton-master
) )
# Add project symbols (macros) # Add project symbols (macros)

View File

@@ -7,7 +7,7 @@ extern "C" {
#include "stm32f1xx_hal.h" #include "stm32f1xx_hal.h"
#include <stdint.h> #include <stdint.h>
#include "mp3_play_index.h"
/* MP3命令定义 */ /* MP3命令定义 */
#define MP3_HEADER 0x7E // 帧头 #define MP3_HEADER 0x7E // 帧头
#define MP3_VERSION 0xFF // 版本号 #define MP3_VERSION 0xFF // 版本号

View File

@@ -6,117 +6,185 @@
#define DELAY 0x80 #define DELAY 0x80
// based on Adafruit ST7735 library for Arduino // based on Adafruit ST7735 library for Arduino
static const uint8_t static const uint8_t init_cmds1[] =
init_cmds1[] = { // Init for 7735R, part 1 (red or green tab) { // Init for 7735R, part 1 (red or green tab)
15, // 15 commands in list: 15, // 15 commands in list:
ST7735_SWRESET, DELAY, // 1: Software reset, 0 args, w/delay ST7735_SWRESET,
DELAY, // 1: Software reset, 0 args, w/delay
150, // 150 ms delay 150, // 150 ms delay
ST7735_SLPOUT, DELAY, // 2: Out of sleep mode, 0 args, w/delay ST7735_SLPOUT,
DELAY, // 2: Out of sleep mode, 0 args, w/delay
255, // 500 ms delay 255, // 500 ms delay
ST7735_FRMCTR1, 3, // 3: Frame rate ctrl - normal mode, 3 args: ST7735_FRMCTR1,
0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D) 3, // 3: Frame rate ctrl - normal mode, 3 args:
ST7735_FRMCTR2, 3, // 4: Frame rate control - idle mode, 3 args: 0x01,
0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D) 0x2C,
ST7735_FRMCTR3, 6, // 5: Frame rate ctrl - partial mode, 6 args: 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D)
0x01, 0x2C, 0x2D, // Dot inversion mode ST7735_FRMCTR2,
0x01, 0x2C, 0x2D, // Line inversion mode 3, // 4: Frame rate control - idle mode, 3 args:
ST7735_INVCTR, 1, // 6: Display inversion ctrl, 1 arg, no delay: 0x01,
0x2C,
0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D)
ST7735_FRMCTR3,
6, // 5: Frame rate ctrl - partial mode, 6 args:
0x01,
0x2C,
0x2D, // Dot inversion mode
0x01,
0x2C,
0x2D, // Line inversion mode
ST7735_INVCTR,
1, // 6: Display inversion ctrl, 1 arg, no delay:
0x07, // No inversion 0x07, // No inversion
ST7735_PWCTR1, 3, // 7: Power control, 3 args, no delay: ST7735_PWCTR1,
3, // 7: Power control, 3 args, no delay:
0xA2, 0xA2,
0x02, // -4.6V 0x02, // -4.6V
0x84, // AUTO mode 0x84, // AUTO mode
ST7735_PWCTR2, 1, // 8: Power control, 1 arg, no delay: ST7735_PWCTR2,
1, // 8: Power control, 1 arg, no delay:
0xC5, // VGH25 = 2.4C VGSEL = -10 VGH = 3 * AVDD 0xC5, // VGH25 = 2.4C VGSEL = -10 VGH = 3 * AVDD
ST7735_PWCTR3, 2, // 9: Power control, 2 args, no delay: ST7735_PWCTR3,
2, // 9: Power control, 2 args, no delay:
0x0A, // Opamp current small 0x0A, // Opamp current small
0x00, // Boost frequency 0x00, // Boost frequency
ST7735_PWCTR4, 2, // 10: Power control, 2 args, no delay: ST7735_PWCTR4,
2, // 10: Power control, 2 args, no delay:
0x8A, // BCLK/2, Opamp current small & Medium low 0x8A, // BCLK/2, Opamp current small & Medium low
0x2A, 0x2A,
ST7735_PWCTR5, 2, // 11: Power control, 2 args, no delay: ST7735_PWCTR5,
0x8A, 0xEE, 2, // 11: Power control, 2 args, no delay:
ST7735_VMCTR1, 1, // 12: Power control, 1 arg, no delay: 0x8A,
0xEE,
ST7735_VMCTR1,
1, // 12: Power control, 1 arg, no delay:
0x0E, 0x0E,
ST7735_INVOFF, 0, // 13: Don't invert display, no args, no delay ST7735_INVOFF,
ST7735_MADCTL, 1, // 14: Memory access control (directions), 1 arg: 0, // 13: Don't invert display, no args, no delay
ST7735_MADCTL,
1, // 14: Memory access control (directions), 1 arg:
ST7735_ROTATION, // row addr/col addr, bottom to top refresh ST7735_ROTATION, // row addr/col addr, bottom to top refresh
ST7735_COLMOD, 1, // 15: set color mode, 1 arg, no delay: ST7735_COLMOD,
1, // 15: set color mode, 1 arg, no delay:
0x05}, // 16-bit color 0x05}, // 16-bit color
#if (defined(ST7735_IS_128X128) || defined(ST7735_IS_160X128)) #if (defined(ST7735_IS_128X128) || defined(ST7735_IS_160X128))
init_cmds2[] = { // Init for 7735R, part 2 (1.44" display) init_cmds2[] =
{ // Init for 7735R, part 2 (1.44" display)
2, // 2 commands in list: 2, // 2 commands in list:
ST7735_CASET, 4, // 1: Column addr set, 4 args, no delay: ST7735_CASET,
0x00, 0x00, // XSTART = 0 4, // 1: Column addr set, 4 args, no delay:
0x00, 0x7F, // XEND = 127 0x00,
ST7735_RASET, 4, // 2: Row addr set, 4 args, no delay: 0x00, // XSTART = 0
0x00, 0x00, // XSTART = 0 0x00,
0x00, 0x7F}, // XEND = 127 0x7F, // XEND = 127
ST7735_RASET,
4, // 2: Row addr set, 4 args, no delay:
0x00,
0x00, // XSTART = 0
0x00,
0x7F}, // XEND = 127
#endif // ST7735_IS_128X128 #endif // ST7735_IS_128X128
#ifdef ST7735_IS_160X80 #ifdef ST7735_IS_160X80
init_cmds2[] = { // Init for 7735S, part 2 (160x80 display) init_cmds2[] =
{ // Init for 7735S, part 2 (160x80 display)
3, // 3 commands in list: 3, // 3 commands in list:
ST7735_CASET, 4, // 1: Column addr set, 4 args, no delay: ST7735_CASET,
0x00, 0x00, // XSTART = 0 4, // 1: Column addr set, 4 args, no delay:
0x00, 0x4F, // XEND = 79 0x00,
ST7735_RASET, 4, // 2: Row addr set, 4 args, no delay: 0x00, // XSTART = 0
0x00, 0x00, // XSTART = 0 0x00,
0x00, 0x9F, // XEND = 159 0x4F, // XEND = 79
ST7735_INVON, 0}, // 3: Invert colors ST7735_RASET,
4, // 2: Row addr set, 4 args, no delay:
0x00,
0x00, // XSTART = 0
0x00,
0x9F, // XEND = 159
ST7735_INVON,
0}, // 3: Invert colors
#endif #endif
init_cmds3[] = { // Init for 7735R, part 3 (red or green tab) init_cmds3[] = { // Init for 7735R, part 3 (red or green tab)
4, // 4 commands in list: 4, // 4 commands in list:
ST7735_GMCTRP1, 16, // 1: Gamma Adjustments (pos. polarity), 16 args, no delay: ST7735_GMCTRP1,
0x02, 0x1c, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2d, 0x29, 0x25, 0x2B, 0x39, 0x00, 0x01, 0x03, 0x10, ST7735_GMCTRN1, 16, // 2: Gamma Adjustments (neg. polarity), 16 args, no delay: 16, // 1: Gamma Adjustments (pos. polarity), 16 args, no delay:
0x03, 0x1d, 0x07, 0x06, 0x2E, 0x2C, 0x29, 0x2D, 0x2E, 0x2E, 0x37, 0x3F, 0x00, 0x00, 0x02, 0x10, ST7735_NORON, DELAY, // 3: Normal display on, no args, w/delay 0x02,
0x1c,
0x07,
0x12,
0x37,
0x32,
0x29,
0x2d,
0x29,
0x25,
0x2B,
0x39,
0x00,
0x01,
0x03,
0x10,
ST7735_GMCTRN1,
16, // 2: Gamma Adjustments (neg. polarity), 16 args, no delay:
0x03,
0x1d,
0x07,
0x06,
0x2E,
0x2C,
0x29,
0x2D,
0x2E,
0x2E,
0x37,
0x3F,
0x00,
0x00,
0x02,
0x10,
ST7735_NORON,
DELAY, // 3: Normal display on, no args, w/delay
10, // 10 ms delay 10, // 10 ms delay
ST7735_DISPON, DELAY, // 4: Main screen turn on, no args w/delay ST7735_DISPON,
DELAY, // 4: Main screen turn on, no args w/delay
100}; // 100 ms delay 100}; // 100 ms delay
static void ST7735_Select() static void ST7735_Select() {
{
HAL_GPIO_WritePin(ST7735_CS_GPIO_Port, ST7735_CS_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(ST7735_CS_GPIO_Port, ST7735_CS_Pin, GPIO_PIN_RESET);
} }
void ST7735_Unselect() void ST7735_Unselect() {
{
HAL_GPIO_WritePin(ST7735_CS_GPIO_Port, ST7735_CS_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(ST7735_CS_GPIO_Port, ST7735_CS_Pin, GPIO_PIN_SET);
} }
static void ST7735_Reset() static void ST7735_Reset() {
{
HAL_GPIO_WritePin(ST7735_RES_GPIO_Port, ST7735_RES_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(ST7735_RES_GPIO_Port, ST7735_RES_Pin, GPIO_PIN_RESET);
HAL_Delay(5); HAL_Delay(5);
HAL_GPIO_WritePin(ST7735_RES_GPIO_Port, ST7735_RES_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(ST7735_RES_GPIO_Port, ST7735_RES_Pin, GPIO_PIN_SET);
} }
static void ST7735_WriteCommand(uint8_t cmd) static void ST7735_WriteCommand(uint8_t cmd) {
{
HAL_GPIO_WritePin(ST7735_DC_GPIO_Port, ST7735_DC_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(ST7735_DC_GPIO_Port, ST7735_DC_Pin, GPIO_PIN_RESET);
HAL_SPI_Transmit(&ST7735_SPI_PORT, &cmd, sizeof(cmd), HAL_MAX_DELAY); HAL_SPI_Transmit(&ST7735_SPI_PORT, &cmd, sizeof(cmd), HAL_MAX_DELAY);
// HAL_SPI_Transmit_DMA(&ST7735_SPI_PORT, &cmd, sizeof(cmd)); // HAL_SPI_Transmit_DMA(&ST7735_SPI_PORT, &cmd, sizeof(cmd));
} }
static void ST7735_WriteData(uint8_t *buff, size_t buff_size) static void ST7735_WriteData(uint8_t *buff, size_t buff_size) {
{
HAL_GPIO_WritePin(ST7735_DC_GPIO_Port, ST7735_DC_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(ST7735_DC_GPIO_Port, ST7735_DC_Pin, GPIO_PIN_SET);
HAL_SPI_Transmit(&ST7735_SPI_PORT, buff, buff_size, HAL_MAX_DELAY); HAL_SPI_Transmit(&ST7735_SPI_PORT, buff, buff_size, HAL_MAX_DELAY);
// HAL_SPI_Transmit_DMA(&ST7735_SPI_PORT, buff, buff_size); // HAL_SPI_Transmit_DMA(&ST7735_SPI_PORT, buff, buff_size);
} }
static void ST7735_ExecuteCommandList(const uint8_t *addr) static void ST7735_ExecuteCommandList(const uint8_t *addr) {
{
uint8_t numCommands, numArgs; uint8_t numCommands, numArgs;
uint16_t ms; uint16_t ms;
numCommands = *addr++; numCommands = *addr++;
while (numCommands--) while (numCommands--) {
{
uint8_t cmd = *addr++; uint8_t cmd = *addr++;
ST7735_WriteCommand(cmd); ST7735_WriteCommand(cmd);
@@ -124,14 +192,12 @@ static void ST7735_ExecuteCommandList(const uint8_t *addr)
// If high bit set, delay follows args // If high bit set, delay follows args
ms = numArgs & DELAY; ms = numArgs & DELAY;
numArgs &= ~DELAY; numArgs &= ~DELAY;
if (numArgs) if (numArgs) {
{
ST7735_WriteData((uint8_t *)addr, numArgs); ST7735_WriteData((uint8_t *)addr, numArgs);
addr += numArgs; addr += numArgs;
} }
if (ms) if (ms) {
{
ms = *addr++; ms = *addr++;
if (ms == 255) if (ms == 255)
ms = 500; ms = 500;
@@ -151,8 +217,8 @@ static void ST7735_ExecuteCommandList(const uint8_t *addr)
* @param x1 显示窗口右下角的X坐标 * @param x1 显示窗口右下角的X坐标
* @param y1 显示窗口右下角的Y坐标 * @param y1 显示窗口右下角的Y坐标
*/ */
static void ST7735_SetAddressWindow(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) static void ST7735_SetAddressWindow(uint8_t x0, uint8_t y0, uint8_t x1,
{ uint8_t y1) {
// column address set // column address set
ST7735_WriteCommand(ST7735_CASET); ST7735_WriteCommand(ST7735_CASET);
// 设置列地址范围,考虑到显示区域的起始偏移 // 设置列地址范围,考虑到显示区域的起始偏移
@@ -178,8 +244,7 @@ static void ST7735_SetAddressWindow(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t
* 以及初始化完成后填充屏幕为黄色。这些操作确保了显示器能够正确配置并进入 * 以及初始化完成后填充屏幕为黄色。这些操作确保了显示器能够正确配置并进入
* 可显示状态。 * 可显示状态。
*/ */
void ST7735_Init() void ST7735_Init() {
{
// 选择ST7735设备准备进行初始化 // 选择ST7735设备准备进行初始化
ST7735_Select(); ST7735_Select();
@@ -216,8 +281,7 @@ void ST7735_Init()
* @param y 纵坐标,表示像素点在显示屏上的垂直位置。 * @param y 纵坐标,表示像素点在显示屏上的垂直位置。
* @param color 像素点的颜色值采用RGB565格式。 * @param color 像素点的颜色值采用RGB565格式。
*/ */
void ST7735_DrawPixel(uint16_t x, uint16_t y, uint16_t color) void ST7735_DrawPixel(uint16_t x, uint16_t y, uint16_t color) {
{
// 检查坐标是否超出显示屏边界 // 检查坐标是否超出显示屏边界
if ((x >= ST7735_WIDTH) || (y >= ST7735_HEIGHT)) if ((x >= ST7735_WIDTH) || (y >= ST7735_HEIGHT))
return; return;
@@ -232,13 +296,13 @@ void ST7735_DrawPixel(uint16_t x, uint16_t y, uint16_t color)
uint8_t data[] = {color >> 8, color & 0xFF}; uint8_t data[] = {color >> 8, color & 0xFF};
// 发送颜色数据到显示屏 // 发送颜色数据到显示屏
//ST7735_WriteData(data, sizeof(data)); // ST7735_WriteData(data, sizeof(data));
HAL_GPIO_WritePin(ST7735_DC_GPIO_Port, ST7735_DC_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(ST7735_DC_GPIO_Port, ST7735_DC_Pin, GPIO_PIN_SET);
HAL_SPI_Transmit_DMA(&ST7735_SPI_PORT, data, sizeof(data)); HAL_SPI_Transmit_DMA(&ST7735_SPI_PORT, data, sizeof(data));
// HAL_SPI_Transmit(&ST7735_SPI_PORT, data, sizeof(data), HAL_MAX_DELAY); // HAL_SPI_Transmit(&ST7735_SPI_PORT, data, sizeof(data), HAL_MAX_DELAY);
// 取消选择显示屏,结束操作 // 取消选择显示屏,结束操作
ST7735_Unselect(); ST7735_Unselect();
@@ -254,8 +318,8 @@ void ST7735_DrawPixel(uint16_t x, uint16_t y, uint16_t color)
* @param color 字符的颜色 * @param color 字符的颜色
* @param bgcolor 背景颜色 * @param bgcolor 背景颜色
*/ */
static void ST7735_WriteChar(uint16_t x, uint16_t y, char ch, FontDef *font, uint16_t color, uint16_t bgcolor) static void ST7735_WriteChar(uint16_t x, uint16_t y, char ch, FontDef *font,
{ uint16_t color, uint16_t bgcolor) {
// 定义循环变量和临时变量 // 定义循环变量和临时变量
uint32_t i, b, j; uint32_t i, b, j;
@@ -263,25 +327,24 @@ static void ST7735_WriteChar(uint16_t x, uint16_t y, char ch, FontDef *font, uin
ST7735_SetAddressWindow(x, y, x + font->width - 1, y + font->height - 1); ST7735_SetAddressWindow(x, y, x + font->width - 1, y + font->height - 1);
// 遍历字符的每一行 // 遍历字符的每一行
for (i = 0; i < font->height; i++) for (i = 0; i < font->height; i++) {
{
// 获取当前行的字形数据 // 获取当前行的字形数据
b = font->data[(ch - 32) * font->height + i]; b = font->data[(ch - 32) * font->height + i];
// 遍历当前行的每一列 // 遍历当前行的每一列
for (j = 0; j < font->width; j++) for (j = 0; j < font->width; j++) {
{
// 检查当前列是否为前景色 // 检查当前列是否为前景色
if ((b << j) & 0x8000) if ((b << j) & 0x8000) {
{
// 是前景色,则发送颜色数据到显示屏 // 是前景色,则发送颜色数据到显示屏
uint8_t data[] = {color >> 8, color & 0xFF}; uint8_t data[] = {color >> 8, color & 0xFF};
ST7735_WriteData(data, sizeof(data)); // ST7735_WriteData(data, sizeof(data));
} HAL_GPIO_WritePin(ST7735_DC_GPIO_Port, ST7735_DC_Pin, GPIO_PIN_SET);
else HAL_SPI_Transmit_DMA(&ST7735_SPI_PORT, data, sizeof(data));
{ } else {
// 不是前景色,则发送背景色数据到显示屏 // 不是前景色,则发送背景色数据到显示屏
uint8_t data[] = {bgcolor >> 8, bgcolor & 0xFF}; uint8_t data[] = {bgcolor >> 8, bgcolor & 0xFF};
ST7735_WriteData(data, sizeof(data)); // ST7735_WriteData(data, sizeof(data));
HAL_GPIO_WritePin(ST7735_DC_GPIO_Port, ST7735_DC_Pin, GPIO_PIN_SET);
HAL_SPI_Transmit_DMA(&ST7735_SPI_PORT, data, sizeof(data));
} }
} }
} }
@@ -290,8 +353,8 @@ static void ST7735_WriteChar(uint16_t x, uint16_t y, char ch, FontDef *font, uin
/* /*
Simpler (and probably slower) implementation: Simpler (and probably slower) implementation:
static void ST7735_WriteChar(uint16_t x, uint16_t y, char ch, FontDef font, uint16_t color) { static void ST7735_WriteChar(uint16_t x, uint16_t y, char ch, FontDef font,
uint32_t i, b, j; uint16_t color) { uint32_t i, b, j;
for(i = 0; i < font->height; i++) { for(i = 0; i < font->height; i++) {
b = font->data[(ch - 32) * font->height + i]; b = font->data[(ch - 32) * font->height + i];
@@ -304,23 +367,19 @@ static void ST7735_WriteChar(uint16_t x, uint16_t y, char ch, FontDef font, uint
} }
*/ */
void ST7735_WriteString(uint16_t x, uint16_t y, const char *str, FontDef *font, uint16_t color, uint16_t bgcolor) void ST7735_WriteString(uint16_t x, uint16_t y, const char *str, FontDef *font,
{ uint16_t color, uint16_t bgcolor) {
ST7735_Select(); ST7735_Select();
while (*str) while (*str) {
{ if (x + font->width >= ST7735_WIDTH) {
if (x + font->width >= ST7735_WIDTH)
{
x = 0; x = 0;
y += font->height; y += font->height;
if (y + font->height >= ST7735_HEIGHT) if (y + font->height >= ST7735_HEIGHT) {
{
break; break;
} }
if (*str == ' ') if (*str == ' ') {
{
// skip spaces in the beginning of the new line // skip spaces in the beginning of the new line
str++; str++;
continue; continue;
@@ -348,8 +407,8 @@ void ST7735_WriteString(uint16_t x, uint16_t y, const char *str, FontDef *font,
* @param h 矩形的高度 * @param h 矩形的高度
* @param color 用于填充矩形的颜色 * @param color 用于填充矩形的颜色
*/ */
void ST7735_FillRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) void ST7735_FillRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h,
{ uint16_t color) {
// clipping // clipping
if ((x >= ST7735_WIDTH) || (y >= ST7735_HEIGHT)) if ((x >= ST7735_WIDTH) || (y >= ST7735_HEIGHT))
return; return;
@@ -368,12 +427,11 @@ void ST7735_FillRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16
// Set the data/command pin to data mode // Set the data/command pin to data mode
HAL_GPIO_WritePin(ST7735_DC_GPIO_Port, ST7735_DC_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(ST7735_DC_GPIO_Port, ST7735_DC_Pin, GPIO_PIN_SET);
// Send the color data for each pixel in the rectangle // Send the color data for each pixel in the rectangle
for (y = h; y > 0; y--) for (y = h; y > 0; y--) {
{ for (x = w; x > 0; x--) {
for (x = w; x > 0; x--)
{
// HAL_SPI_Transmit(&ST7735_SPI_PORT, data, sizeof(data), HAL_MAX_DELAY); // HAL_SPI_Transmit(&ST7735_SPI_PORT, data, sizeof(data),
// HAL_MAX_DELAY);
HAL_SPI_Transmit_DMA(&ST7735_SPI_PORT, data, sizeof(data)); HAL_SPI_Transmit_DMA(&ST7735_SPI_PORT, data, sizeof(data));
} }
} }
@@ -384,8 +442,8 @@ void ST7735_FillRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16
// Fill a rectangle area on the ST7735 LCD screen with a specified color // Fill a rectangle area on the ST7735 LCD screen with a specified color
// This function is optimized for speed, suitable for filling large areas // This function is optimized for speed, suitable for filling large areas
void ST7735_FillRectangleFast(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) void ST7735_FillRectangleFast(uint16_t x, uint16_t y, uint16_t w, uint16_t h,
{ uint16_t color) {
// clipping // clipping
if ((x >= ST7735_WIDTH) || (y >= ST7735_HEIGHT)) if ((x >= ST7735_WIDTH) || (y >= ST7735_HEIGHT))
return; return;
@@ -417,10 +475,11 @@ void ST7735_FillRectangleFast(uint16_t x, uint16_t y, uint16_t w, uint16_t h, ui
* *
* @param color 要填充的颜色值。 * @param color 要填充的颜色值。
*/ */
void ST7735_FillScreen(uint16_t color) void ST7735_FillScreen(uint16_t color) {
{ // 使用FillRectangle函数填充整个屏幕起始坐标为(0,
// 使用FillRectangle函数填充整个屏幕起始坐标为(0, 0),覆盖整个屏幕宽度和高度。 // 0),覆盖整个屏幕宽度和高度。
ST7735_FillRectangle(0, 0, ST7735_WIDTH, ST7735_HEIGHT, color); // ST7735_FillRectangle(0, 0, ST7735_WIDTH, ST7735_HEIGHT, color);
ST7735_FillRectangleFast(0, 0, ST7735_WIDTH, ST7735_HEIGHT, color);
} }
/** /**
@@ -429,12 +488,12 @@ void ST7735_FillScreen(uint16_t color)
* 使用指定的颜色填充整个屏幕。该函数通过调用ST7735_FillRectangleFast函数 * 使用指定的颜色填充整个屏幕。该函数通过调用ST7735_FillRectangleFast函数
* 设置填充区域为整个屏幕的宽度和高度来实现快速填充屏幕的效果。 * 设置填充区域为整个屏幕的宽度和高度来实现快速填充屏幕的效果。
* *
* @param color 填充屏幕所使用的颜色值。这是一个16位的颜色值适用于ST7735显示屏。 * @param color
* 填充屏幕所使用的颜色值。这是一个16位的颜色值适用于ST7735显示屏。
*/ */
void ST7735_FillScreenFast(uint16_t color) void ST7735_FillScreenFast(uint16_t color) {
{ // 调用快速填充矩形函数,参数为(起始X坐标, 起始Y坐标, 屏幕宽度, 屏幕高度,
// 调用快速填充矩形函数,参数为(起始X坐标, 起始Y坐标, 屏幕宽度, 屏幕高度, 颜色) // 颜色) 用于填充整个屏幕为指定的颜色
// 用于填充整个屏幕为指定的颜色
ST7735_FillRectangleFast(0, 0, ST7735_WIDTH, ST7735_HEIGHT, color); ST7735_FillRectangleFast(0, 0, ST7735_WIDTH, ST7735_HEIGHT, color);
} }
@@ -451,8 +510,8 @@ void ST7735_FillScreenFast(uint16_t color)
* @param h 图像的高度 * @param h 图像的高度
* @param data 图像数据指针图像数据是16位的RGB565格式 * @param data 图像数据指针图像数据是16位的RGB565格式
*/ */
void ST7735_DrawImage(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint16_t *data) void ST7735_DrawImage(uint16_t x, uint16_t y, uint16_t w, uint16_t h,
{ const uint16_t *data) {
// 检查图像的起始坐标是否超出液晶屏的边界 // 检查图像的起始坐标是否超出液晶屏的边界
if ((x >= ST7735_WIDTH) || (y >= ST7735_HEIGHT)) if ((x >= ST7735_WIDTH) || (y >= ST7735_HEIGHT))
return; return;
@@ -481,10 +540,10 @@ void ST7735_DrawImage(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint
/** /**
* ST7735_InvertColors函数用于控制ST7735显示屏的颜色反转功能。 * ST7735_InvertColors函数用于控制ST7735显示屏的颜色反转功能。
* *
* @param invert 一个布尔值决定是否反转显示屏的颜色。如果invert为true颜色将反转如果为false颜色将保持正常。 * @param invert
* 一个布尔值决定是否反转显示屏的颜色。如果invert为true颜色将反转如果为false颜色将保持正常。
*/ */
void ST7735_InvertColors(bool invert) void ST7735_InvertColors(bool invert) {
{
// 选择ST7735显示屏以进行通信。 // 选择ST7735显示屏以进行通信。
ST7735_Select(); ST7735_Select();
@@ -503,8 +562,7 @@ void ST7735_InvertColors(bool invert)
* *
* @param gamma 伽马曲线定义,指定要设置的伽马曲线 * @param gamma 伽马曲线定义,指定要设置的伽马曲线
*/ */
void ST7735_SetGamma(GammaDef gamma) void ST7735_SetGamma(GammaDef gamma) {
{
// 选择ST7735显示器以进行通信 // 选择ST7735显示器以进行通信
ST7735_Select(); ST7735_Select();

View File

@@ -274,6 +274,11 @@ extern "C"
// 解释在使用ST7735显示屏之前必须调用此函数进行初始化设置 // 解释在使用ST7735显示屏之前必须调用此函数进行初始化设置
void ST7735_Init(void); void ST7735_Init(void);
// 启用/禁用DMA传输模式
// 参数enable - true启用DMA模式false禁用DMA模式
// 解释初始化完成后调用此函数启用DMA模式可大幅提升刷屏速度
void ST7735_EnableDMA(bool enable);
// 在指定位置绘制一个像素点 // 在指定位置绘制一个像素点
// 参数x - 像素点的X坐标y - 像素点的Y坐标color - 像素点的颜色 // 参数x - 像素点的X坐标y - 像素点的Y坐标color - 像素点的颜色
// 解释:此函数用于在显示屏上的特定位置绘制一个单一颜色的像素点 // 解释:此函数用于在显示屏上的特定位置绘制一个单一颜色的像素点

View File

@@ -2,6 +2,7 @@
#include "cmsis_os.h" #include "cmsis_os.h"
#include "cmsis_os2.h" #include "cmsis_os2.h"
#include "elog.h" #include "elog.h"
#include "mp3_driver.h" // 添加MP3模块头文件
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -201,16 +202,19 @@ uint8_t WIFI_Connect_WiFi(const char *ssid, const char *password,
elog_i(TAG, "等待WiFi基础连接响应..."); elog_i(TAG, "等待WiFi基础连接响应...");
if (!WIFI_WaitEvent("OK", "ERROR", 3000)) { if (!WIFI_WaitEvent("OK", "ERROR", 3000)) {
elog_e(TAG, "WiFi基础连接失败"); elog_e(TAG, "WiFi基础连接失败");
MP3_Play(WIFI_CONNECT_FAIL); // WiFi连接失败播放提示音
return 0; return 0;
} }
elog_i(TAG, "等待WiFi详细连接结果..."); elog_i(TAG, "等待WiFi详细连接结果...");
if (!WIFI_WaitEvent("+CWJAP:1", "+CWJAP:0", timeout_ms)) { if (!WIFI_WaitEvent("+CWJAP:1", "+CWJAP:0", timeout_ms)) {
elog_e(TAG, "WiFi详细连接失败"); elog_e(TAG, "WiFi详细连接失败");
MP3_Play(WIFI_CONNECT_FAIL); // WiFi连接失败播放提示音
return 0; return 0;
} }
elog_i(TAG, "WiFi连接成功"); elog_i(TAG, "WiFi连接成功");
MP3_Play(WIFI_CONNECT_OK); // WiFi连接成功播放提示音
return 1; return 1;
} }

View File

@@ -0,0 +1,292 @@
/*
* Copyright (c) 2016 Zibin Zheng <znbin@qq.com>
* All rights reserved
*/
#include "multi_button.h"
// Macro for callback execution with null check
#define EVENT_CB(ev) do { if(handle->cb[ev]) handle->cb[ev](handle); } while(0)
// Button handle list head
static Button* head_handle = NULL;
// Forward declarations
static void button_handler(Button* handle);
static inline uint8_t button_read_level(Button* handle);
/**
* @brief Initialize the button struct handle
* @param handle: the button handle struct
* @param pin_level: read the HAL GPIO of the connected button level
* @param active_level: pressed GPIO level
* @param button_id: the button id
* @retval None
*/
void button_init(Button* handle, uint8_t(*pin_level)(uint8_t), uint8_t active_level, uint8_t button_id)
{
if (!handle || !pin_level) return; // parameter validation
memset(handle, 0, sizeof(Button));
handle->event = (uint8_t)BTN_NONE_PRESS;
handle->hal_button_level = pin_level;
handle->button_level = !active_level; // initialize to opposite of active level
handle->active_level = active_level;
handle->button_id = button_id;
handle->state = BTN_STATE_IDLE;
}
/**
* @brief Attach the button event callback function
* @param handle: the button handle struct
* @param event: trigger event type
* @param cb: callback function
* @retval None
*/
void button_attach(Button* handle, ButtonEvent event, BtnCallback cb)
{
if (!handle || event >= BTN_EVENT_COUNT) return; // parameter validation
handle->cb[event] = cb;
}
/**
* @brief Detach the button event callback function
* @param handle: the button handle struct
* @param event: trigger event type
* @retval None
*/
void button_detach(Button* handle, ButtonEvent event)
{
if (!handle || event >= BTN_EVENT_COUNT) return; // parameter validation
handle->cb[event] = NULL;
}
/**
* @brief Get the button event that happened
* @param handle: the button handle struct
* @retval button event
*/
ButtonEvent button_get_event(Button* handle)
{
if (!handle) return BTN_NONE_PRESS;
return (ButtonEvent)(handle->event);
}
/**
* @brief Get the repeat count of button presses
* @param handle: the button handle struct
* @retval repeat count
*/
uint8_t button_get_repeat_count(Button* handle)
{
if (!handle) return 0;
return handle->repeat;
}
/**
* @brief Reset button state to idle
* @param handle: the button handle struct
* @retval None
*/
void button_reset(Button* handle)
{
if (!handle) return;
handle->state = BTN_STATE_IDLE;
handle->ticks = 0;
handle->repeat = 0;
handle->event = (uint8_t)BTN_NONE_PRESS;
handle->debounce_cnt = 0;
}
/**
* @brief Check if button is currently pressed
* @param handle: the button handle struct
* @retval 1: pressed, 0: not pressed, -1: error
*/
int button_is_pressed(Button* handle)
{
if (!handle) return -1;
return (handle->button_level == handle->active_level) ? 1 : 0;
}
/**
* @brief Read button level with inline optimization
* @param handle: the button handle struct
* @retval button level
*/
static inline uint8_t button_read_level(Button* handle)
{
return handle->hal_button_level(handle->button_id);
}
/**
* @brief Button driver core function, driver state machine
* @param handle: the button handle struct
* @retval None
*/
static void button_handler(Button* handle)
{
uint8_t read_gpio_level = button_read_level(handle);
// Increment ticks counter when not in idle state
if (handle->state > BTN_STATE_IDLE) {
handle->ticks++;
}
/*------------Button debounce handling---------------*/
if (read_gpio_level != handle->button_level) {
// Continue reading same new level for debounce
if (++(handle->debounce_cnt) >= DEBOUNCE_TICKS) {
handle->button_level = read_gpio_level;
handle->debounce_cnt = 0;
}
} else {
// Level not changed, reset counter
handle->debounce_cnt = 0;
}
/*-----------------State machine-------------------*/
switch (handle->state) {
case BTN_STATE_IDLE:
if (handle->button_level == handle->active_level) {
// Button press detected
handle->event = (uint8_t)BTN_PRESS_DOWN;
EVENT_CB(BTN_PRESS_DOWN);
handle->ticks = 0;
handle->repeat = 1;
handle->state = BTN_STATE_PRESS;
} else {
handle->event = (uint8_t)BTN_NONE_PRESS;
}
break;
case BTN_STATE_PRESS:
if (handle->button_level != handle->active_level) {
// Button released
handle->event = (uint8_t)BTN_PRESS_UP;
EVENT_CB(BTN_PRESS_UP);
handle->ticks = 0;
handle->state = BTN_STATE_RELEASE;
} else if (handle->ticks > LONG_TICKS) {
// Long press detected
handle->event = (uint8_t)BTN_LONG_PRESS_START;
EVENT_CB(BTN_LONG_PRESS_START);
handle->state = BTN_STATE_LONG_HOLD;
}
break;
case BTN_STATE_RELEASE:
if (handle->button_level == handle->active_level) {
// Button pressed again
handle->event = (uint8_t)BTN_PRESS_DOWN;
EVENT_CB(BTN_PRESS_DOWN);
if (handle->repeat < PRESS_REPEAT_MAX_NUM) {
handle->repeat++;
}
EVENT_CB(BTN_PRESS_REPEAT);
handle->ticks = 0;
handle->state = BTN_STATE_REPEAT;
} else if (handle->ticks > SHORT_TICKS) {
// Timeout reached, determine click type
if (handle->repeat == 1) {
handle->event = (uint8_t)BTN_SINGLE_CLICK;
EVENT_CB(BTN_SINGLE_CLICK);
} else if (handle->repeat == 2) {
handle->event = (uint8_t)BTN_DOUBLE_CLICK;
EVENT_CB(BTN_DOUBLE_CLICK);
}
handle->state = BTN_STATE_IDLE;
}
break;
case BTN_STATE_REPEAT:
if (handle->button_level != handle->active_level) {
// Button released
handle->event = (uint8_t)BTN_PRESS_UP;
EVENT_CB(BTN_PRESS_UP);
if (handle->ticks < SHORT_TICKS) {
handle->ticks = 0;
handle->state = BTN_STATE_RELEASE; // Continue waiting for more presses
} else {
handle->state = BTN_STATE_IDLE; // End of sequence
}
} else if (handle->ticks > SHORT_TICKS) {
// Held down too long, treat as normal press
handle->state = BTN_STATE_PRESS;
}
break;
case BTN_STATE_LONG_HOLD:
if (handle->button_level == handle->active_level) {
// Continue holding
handle->event = (uint8_t)BTN_LONG_PRESS_HOLD;
EVENT_CB(BTN_LONG_PRESS_HOLD);
} else {
// Released from long press
handle->event = (uint8_t)BTN_PRESS_UP;
EVENT_CB(BTN_PRESS_UP);
handle->state = BTN_STATE_IDLE;
}
break;
default:
// Invalid state, reset to idle
handle->state = BTN_STATE_IDLE;
break;
}
}
/**
* @brief Start the button work, add the handle into work list
* @param handle: target handle struct
* @retval 0: succeed, -1: already exist, -2: invalid parameter
*/
int button_start(Button* handle)
{
if (!handle) return -2; // invalid parameter
Button* target = head_handle;
while (target) {
if (target == handle) return -1; // already exist
target = target->next;
}
handle->next = head_handle;
head_handle = handle;
return 0;
}
/**
* @brief Stop the button work, remove the handle from work list
* @param handle: target handle struct
* @retval None
*/
void button_stop(Button* handle)
{
if (!handle) return; // parameter validation
Button** curr;
for (curr = &head_handle; *curr; ) {
Button* entry = *curr;
if (entry == handle) {
*curr = entry->next;
entry->next = NULL; // clear next pointer
return;
} else {
curr = &entry->next;
}
}
}
/**
* @brief Background ticks, timer repeat invoking interval 5ms
* @param None
* @retval None
*/
void button_ticks(void)
{
Button* target;
for (target = head_handle; target; target = target->next) {
button_handler(target);
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright (c) 2016 Zibin Zheng <znbin@qq.com>
* All rights reserved
*/
#ifndef _MULTI_BUTTON_H_
#define _MULTI_BUTTON_H_
#include <stdint.h>
#include <string.h>
// Configuration constants - can be modified according to your needs
#define TICKS_INTERVAL 5 // ms - timer interrupt interval
#define DEBOUNCE_TICKS 3 // MAX 7 (0 ~ 7) - debounce filter depth
#define SHORT_TICKS (300 / TICKS_INTERVAL) // short press threshold
#define LONG_TICKS (1000 / TICKS_INTERVAL) // long press threshold
#define PRESS_REPEAT_MAX_NUM 15 // maximum repeat counter value
// Forward declaration
typedef struct _Button Button;
// Button callback function type
typedef void (*BtnCallback)(Button* btn_handle);
// Button event types
typedef enum {
BTN_PRESS_DOWN = 0, // button pressed down
BTN_PRESS_UP, // button released
BTN_PRESS_REPEAT, // repeated press detected
BTN_SINGLE_CLICK, // single click completed
BTN_DOUBLE_CLICK, // double click completed
BTN_LONG_PRESS_START, // long press started
BTN_LONG_PRESS_HOLD, // long press holding
BTN_EVENT_COUNT, // total number of events
BTN_NONE_PRESS // no event
} ButtonEvent;
// Button state machine states
typedef enum {
BTN_STATE_IDLE = 0, // idle state
BTN_STATE_PRESS, // pressed state
BTN_STATE_RELEASE, // released state waiting for timeout
BTN_STATE_REPEAT, // repeat press state
BTN_STATE_LONG_HOLD // long press hold state
} ButtonState;
// Button structure
struct _Button {
uint16_t ticks; // tick counter
uint8_t repeat : 4; // repeat counter (0-15)
uint8_t event : 4; // current event (0-15)
uint8_t state : 3; // state machine state (0-7)
uint8_t debounce_cnt : 3; // debounce counter (0-7)
uint8_t active_level : 1; // active GPIO level (0 or 1)
uint8_t button_level : 1; // current button level
uint8_t button_id; // button identifier
uint8_t (*hal_button_level)(uint8_t button_id); // HAL function to read GPIO
BtnCallback cb[BTN_EVENT_COUNT]; // callback function array
Button* next; // next button in linked list
};
#ifdef __cplusplus
extern "C" {
#endif
// Public API functions
void button_init(Button* handle, uint8_t(*pin_level)(uint8_t), uint8_t active_level, uint8_t button_id);
void button_attach(Button* handle, ButtonEvent event, BtnCallback cb);
void button_detach(Button* handle, ButtonEvent event);
ButtonEvent button_get_event(Button* handle);
int button_start(Button* handle);
void button_stop(Button* handle);
void button_ticks(void);
// Utility functions
uint8_t button_get_repeat_count(Button* handle);
void button_reset(Button* handle);
int button_is_pressed(Button* handle);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -63,8 +63,8 @@ void Error_Handler(void);
#define KEY2_GPIO_Port GPIOC #define KEY2_GPIO_Port GPIOC
#define KEY3_Pin GPIO_PIN_2 #define KEY3_Pin GPIO_PIN_2
#define KEY3_GPIO_Port GPIOC #define KEY3_GPIO_Port GPIOC
#define KEY3C3_Pin GPIO_PIN_3 #define KEY4_Pin GPIO_PIN_3
#define KEY3C3_GPIO_Port GPIOC #define KEY4_GPIO_Port GPIOC
#define HX711_DOUT_Pin GPIO_PIN_2 #define HX711_DOUT_Pin GPIO_PIN_2
#define HX711_DOUT_GPIO_Port GPIOA #define HX711_DOUT_GPIO_Port GPIOA
#define HX711_SCK_Pin GPIO_PIN_3 #define HX711_SCK_Pin GPIO_PIN_3

View File

@@ -19,10 +19,9 @@
/* Includes ------------------------------------------------------------------*/ /* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h" #include "FreeRTOS.h"
#include "cmsis_os.h"
#include "main.h"
#include "task.h" #include "task.h"
#include "main.h"
#include "cmsis_os.h"
/* Private includes ----------------------------------------------------------*/ /* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */ /* USER CODE BEGIN Includes */
@@ -30,10 +29,10 @@
#include "dx_wf_24.h" #include "dx_wf_24.h"
#include "elog.h" #include "elog.h"
#include "mp3_driver.h" #include "mp3_driver.h"
#include "mp3_play_index.h"
#include "multi_button.h"
#include "spi_st7735s.h" #include "spi_st7735s.h"
#include "stdio.h" #include "stdio.h"
#include "mp3_play_index.h"
/* USER CODE END Includes */ /* USER CODE END Includes */
@@ -45,7 +44,11 @@
/* Private define ------------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */ /* USER CODE BEGIN PD */
#define TAG "Main" #define TAG "Main"
/******************** 按键功能定义 ********************/
#define KEY1_MODE_SWITCH // 按键1模式切换自动/手动)
#define KEY2_MANUAL_FEED // 按键2手动喂食仅手动模式有效
#define KEY3_DISPLAY_NEXT // 按键3切换显示界面
#define KEY4_TIME_SET // 按键4长按设置时间
/* USER CODE END PD */ /* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/ /* Private macro -------------------------------------------------------------*/
@@ -56,37 +59,74 @@
/* Private variables ---------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */ /* USER CODE BEGIN Variables */
// LCD页面枚举定义
typedef enum {
LCD_PAGE_TIME = 0, // 时间显示页面
LCD_PAGE_TEMP_HUMI, // 温湿度页面
LCD_PAGE_FOOD_WEIGHT, // 食物重量页面
LCD_PAGE_WATER_LEVEL, // 水位页面
LCD_PAGE_SYSTEM_STATUS // 系统状态页面
} LCD_Page_t;
// 传感器数据结构体
typedef struct {
float temperature; // 温度值
float humidity; // 湿度值
float food_weight; // 食物重量
uint8_t water_level; // 水位状态 (0=无水, 1=有水)
uint8_t system_mode; // 系统模式 (0=手动, 1=自动)
} Sensor_Data_t;
// 全局变量
static LCD_Page_t current_page = LCD_PAGE_TIME; // 当前显示页面
static Sensor_Data_t sensor_data = {0}; // 传感器数据
/* USER CODE END Variables */ /* USER CODE END Variables */
/* Definitions for defaultTask */ /* Definitions for defaultTask */
osThreadId_t defaultTaskHandle; osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = { const osThreadAttr_t defaultTask_attributes = {
.name = "defaultTask", .name = "defaultTask",
.stack_size = 256 * 4, .stack_size = 256 * 4,
.priority = (osPriority_t)osPriorityHigh1, .priority = (osPriority_t) osPriorityHigh1,
}; };
/* Definitions for wifi_mqtt */ /* Definitions for wifi_mqtt */
osThreadId_t wifi_mqttHandle; osThreadId_t wifi_mqttHandle;
const osThreadAttr_t wifi_mqtt_attributes = { const osThreadAttr_t wifi_mqtt_attributes = {
.name = "wifi_mqtt", .name = "wifi_mqtt",
.stack_size = 3000 * 4, .stack_size = 3000 * 4,
.priority = (osPriority_t)osPriorityHigh, .priority = (osPriority_t) osPriorityHigh,
}; };
/* Definitions for LCD_SHOW_Task */ /* Definitions for LCD_SHOW_Task */
osThreadId_t LCD_SHOW_TaskHandle; osThreadId_t LCD_SHOW_TaskHandle;
const osThreadAttr_t LCD_SHOW_Task_attributes = { const osThreadAttr_t LCD_SHOW_Task_attributes = {
.name = "LCD_SHOW_Task", .name = "LCD_SHOW_Task",
.stack_size = 1024 * 4, .stack_size = 1024 * 4,
.priority = (osPriority_t)osPriorityHigh, .priority = (osPriority_t) osPriorityHigh,
};
/* Definitions for button */
osThreadId_t buttonHandle;
const osThreadAttr_t button_attributes = {
.name = "button",
.stack_size = 512 * 4,
.priority = (osPriority_t) osPriorityRealtime2,
}; };
/* Private function prototypes -----------------------------------------------*/ /* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */ /* USER CODE BEGIN FunctionPrototypes */
void LCD_NextPage(void);
void LCD_PrevPage(void);
void LCD_SetPage(LCD_Page_t page);
LCD_Page_t LCD_GetCurrentPage(void);
void LCD_UpdateSensorData(float temp, float humi, float weight, uint8_t water,
uint8_t mode);
Sensor_Data_t *LCD_GetSensorData(void);
void user_button_init(void);
/* USER CODE END FunctionPrototypes */ /* USER CODE END FunctionPrototypes */
void StartDefaultTask(void *argument); void StartDefaultTask(void *argument);
extern void wifi_task_mqtt(void *argument); extern void wifi_task_mqtt(void *argument);
void LCD_Task(void *argument); void LCD_Task(void *argument);
void button_task(void *argument);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */ void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
@@ -118,8 +158,7 @@ void MX_FREERTOS_Init(void) {
/* Create the thread(s) */ /* Create the thread(s) */
/* creation of defaultTask */ /* creation of defaultTask */
defaultTaskHandle = defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
/* creation of wifi_mqtt */ /* creation of wifi_mqtt */
wifi_mqttHandle = osThreadNew(wifi_task_mqtt, NULL, &wifi_mqtt_attributes); wifi_mqttHandle = osThreadNew(wifi_task_mqtt, NULL, &wifi_mqtt_attributes);
@@ -127,6 +166,9 @@ void MX_FREERTOS_Init(void) {
/* creation of LCD_SHOW_Task */ /* creation of LCD_SHOW_Task */
LCD_SHOW_TaskHandle = osThreadNew(LCD_Task, NULL, &LCD_SHOW_Task_attributes); LCD_SHOW_TaskHandle = osThreadNew(LCD_Task, NULL, &LCD_SHOW_Task_attributes);
/* creation of button */
buttonHandle = osThreadNew(button_task, NULL, &button_attributes);
/* USER CODE BEGIN RTOS_THREADS */ /* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */ /* add threads, ... */
/* USER CODE END RTOS_THREADS */ /* USER CODE END RTOS_THREADS */
@@ -134,6 +176,7 @@ void MX_FREERTOS_Init(void) {
/* USER CODE BEGIN RTOS_EVENTS */ /* USER CODE BEGIN RTOS_EVENTS */
/* add events, ... */ /* add events, ... */
/* USER CODE END RTOS_EVENTS */ /* USER CODE END RTOS_EVENTS */
} }
/* USER CODE BEGIN Header_StartDefaultTask */ /* USER CODE BEGIN Header_StartDefaultTask */
@@ -143,7 +186,8 @@ void MX_FREERTOS_Init(void) {
* @retval None * @retval None
*/ */
/* USER CODE END Header_StartDefaultTask */ /* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument) { void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN StartDefaultTask */ /* USER CODE BEGIN StartDefaultTask */
// 1. 打开运行灯 // 1. 打开运行灯
Device_Control(DEVICE_LED_RUN, 1); Device_Control(DEVICE_LED_RUN, 1);
@@ -159,7 +203,7 @@ void StartDefaultTask(void *argument) {
} }
elog_i("MP3", "模块初始化完成"); elog_i("MP3", "模块初始化完成");
MP3_Play(SYS_POWER_ON); MP3_Play(SYS_POWER_ON); // 播放系统启动音
/* Infinite loop */ /* Infinite loop */
for (;;) { for (;;) {
@@ -179,36 +223,117 @@ void StartDefaultTask(void *argument) {
* @retval None * @retval None
*/ */
/* USER CODE END Header_LCD_Task */ /* USER CODE END Header_LCD_Task */
void LCD_Task(void *argument) { void LCD_Task(void *argument)
{
/* USER CODE BEGIN LCD_Task */ /* USER CODE BEGIN LCD_Task */
char time_str[16]; char display_str[32];
SNTP_Time_t now; SNTP_Time_t now;
uint16_t text_color = ST7735_BLACK; uint16_t text_color = ST7735_WHITE;
uint16_t bg_color = ST7735_WHITE; uint16_t bg_color = ST7735_BLACK;
// 显示区域参数 - 适配160x80横屏
// 宽度160px高度80px使用较小字体确保内容完整显示
// 初始化传感器数据 - 全部设置为NC状态
sensor_data.temperature = 0.0f; // NC - 未检测到温度
sensor_data.humidity = 0.0f; // NC - 未检测到湿度
sensor_data.food_weight = 0.0f; // NC - 未检测到重量
sensor_data.water_level = 0; // NC - 未检测到水
sensor_data.system_mode = 1; // 自动模式
// 显示时间的区域大小假设字体16x24
const uint16_t x = 0;
const uint16_t y = 0;
const uint16_t w = 6 * 16; // 6个字符每个字符16px宽
const uint16_t h = 24; // 高度与字体匹配
/* Infinite loop */ /* Infinite loop */
for (;;) { for (;;) {
// 根据当前页面显示不同内容
switch (current_page) {
case LCD_PAGE_TIME:
// 时间显示页面
ST7735_FillScreen(bg_color);
ST7735_WriteString(2, 2, "Time", &Font_7x10, text_color, bg_color);
if (WIFI_Is_Time_Valid()) { if (WIFI_Is_Time_Valid()) {
WIFI_Get_SNTP_Time(); WIFI_Get_SNTP_Time();
now = WIFI_Get_Current_Time(); now = WIFI_Get_Current_Time();
if (now.valid) { if (now.valid) {
snprintf(time_str, sizeof(time_str), "%02d:%02d:%02d", now.hour, snprintf(display_str, sizeof(display_str), "%02d:%02d:%02d", now.hour,
now.minute, now.second); now.minute, now.second);
} else { } else {
snprintf(time_str, sizeof(time_str), "--:--:--"); snprintf(display_str, sizeof(display_str), "--:--:--");
}
ST7735_WriteString(10, 20, display_str, &Font_16x26, text_color, bg_color);
} else {
ST7735_WriteString(10, 20, "--:--:--", &Font_16x26, text_color, bg_color);
}
break;
case LCD_PAGE_TEMP_HUMI:
// 温湿度页面
ST7735_FillScreen(bg_color);
ST7735_WriteString(2, 2, "Temp & Humi", &Font_7x10, text_color, bg_color);
// 显示温度
snprintf(display_str, sizeof(display_str), "T: %.1f C", sensor_data.temperature);
ST7735_WriteString(5, 20, display_str, &Font_11x18, text_color, bg_color);
// 显示湿度
snprintf(display_str, sizeof(display_str), "H: %.1f %%", sensor_data.humidity);
ST7735_WriteString(5, 45, display_str, &Font_11x18, text_color, bg_color);
break;
case LCD_PAGE_FOOD_WEIGHT:
// 食物重量页面
ST7735_FillScreen(bg_color);
ST7735_WriteString(2, 2, "Food Weight", &Font_7x10, text_color, bg_color);
// 显示食物重量
snprintf(display_str, sizeof(display_str), "%.1f g", sensor_data.food_weight);
ST7735_WriteString(25, 20, display_str, &Font_16x26, text_color, bg_color);
// 显示状态
if (sensor_data.food_weight < 50.0f) {
ST7735_WriteString(20, 55, "Low", &Font_11x18, ST7735_RED, bg_color);
} else if (sensor_data.food_weight < 100.0f) {
ST7735_WriteString(15, 55, "Medium", &Font_11x18, ST7735_YELLOW, bg_color);
} else {
ST7735_WriteString(20, 55, "Good", &Font_11x18, ST7735_GREEN, bg_color);
}
break;
case LCD_PAGE_WATER_LEVEL:
// 水位页面
ST7735_FillScreen(bg_color);
ST7735_WriteString(2, 2, "Water Level", &Font_7x10, text_color, bg_color);
// 根据水位传感器状态显示
if (sensor_data.water_level == 0) {
ST7735_WriteString(20, 20, "NO WATER", &Font_16x26, ST7735_RED, bg_color);
ST7735_WriteString(35, 55, "Add water", &Font_11x18, ST7735_YELLOW, bg_color);
} else {
ST7735_WriteString(50, 25, "OK", &Font_16x26, ST7735_GREEN, bg_color);
ST7735_WriteString(35, 55, "Water OK", &Font_11x18, text_color, bg_color);
}
break;
case LCD_PAGE_SYSTEM_STATUS:
// 系统状态页面
ST7735_FillScreen(bg_color);
ST7735_WriteString(2, 2, "System Status", &Font_7x10, text_color, bg_color);
// 显示WiFi状态
if (WIFI_Is_Time_Valid()) {
ST7735_WriteString(5, 20, "WiFi: OK", &Font_11x18, ST7735_GREEN, bg_color);
} else {
ST7735_WriteString(5, 20, "WiFi: OFF", &Font_11x18, ST7735_RED, bg_color);
} }
// 先清空显示区域 // 显示系统模式
ST7735_FillRectangleFast(x, y, w, h, bg_color); if (sensor_data.system_mode) {
ST7735_WriteString(5, 45, "Mode: AUTO", &Font_11x18, text_color, bg_color);
// 写入时间 } else {
ST7735_WriteString(x, y, time_str, &Font_16x26, text_color, bg_color); ST7735_WriteString(5, 45, "Mode: MANUAL", &Font_11x18, text_color, bg_color);
}
break;
} }
osDelay(1000); osDelay(1000);
@@ -216,7 +341,173 @@ void LCD_Task(void *argument) {
/* USER CODE END LCD_Task */ /* USER CODE END LCD_Task */
} }
/* USER CODE BEGIN Header_button_task */
/**
* @brief Function implementing the button thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_button_task */
void button_task(void *argument)
{
/* USER CODE BEGIN button_task */
user_button_init();
/* Infinite loop */
for(;;)
{
button_ticks();
osDelay(5);
}
/* USER CODE END button_task */
}
/* Private application code --------------------------------------------------*/ /* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */ /* USER CODE BEGIN Application */
// 按键库实现部分//
/* USER CODE BEGIN KEY Prototypes */
static Button KEY1; // 按键1
static Button KEY2; // 按键2
static Button KEY3; // 按键3
static Button KEY4; // 按键4
uint8_t read_button_gpio(uint8_t button_id) {
switch (button_id) {
case 1:
return HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin);
break;
case 2:
return HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin);
break;
case 3:
return HAL_GPIO_ReadPin(KEY3_GPIO_Port, KEY3_Pin);
break;
case 4:
return HAL_GPIO_ReadPin(KEY4_GPIO_Port, KEY4_Pin);
break;
default:
return 0;
}
}
void key1_single_click_handler(Button *btn) { elog_i("KEY", "按键1单击"); }
void key2_single_click_handler(Button *btn) { elog_i("KEY", "按键2单击"); }
void key3_single_click_handler(Button *btn) {
elog_i("KEY", "按键3单击");
LCD_NextPage();
}
void key4_single_click_handler(Button *btn) { elog_i("KEY", "按键4单击"); }
void user_button_init(void) {
// 初始化按键 (active_level: 0=低电平有效, 1=高电平有效)
button_init(&KEY1, read_button_gpio, 0, 1);
button_init(&KEY2, read_button_gpio, 0, 2);
button_init(&KEY3, read_button_gpio, 0, 3);
button_init(&KEY4, read_button_gpio, 0, 4);
elog_i("BUTTON", "按键初始化完成");
// 设置按键回调函数
button_attach(&KEY1, BTN_SINGLE_CLICK, key1_single_click_handler);
button_attach(&KEY2, BTN_SINGLE_CLICK, key2_single_click_handler);
button_attach(&KEY3, BTN_SINGLE_CLICK, key3_single_click_handler);
button_attach(&KEY4, BTN_SINGLE_CLICK, key4_single_click_handler);
elog_i("BUTTON", "按键回调函数设置完成");
// 启动按键任务
button_start(&KEY1);
button_start(&KEY2);
button_start(&KEY3);
button_start(&KEY4);
elog_i("BUTTON", "按键任务已启动");
}
/* USER CODE END KEY Prototypes */
/* USER CODE BEGIN LCD_Page_Functions */
/**
* @brief 切换到下一个显示页面
* @retval None
*/
void LCD_NextPage(void) {
current_page = (current_page + 1) % 5; // 循环切换5个页面
elog_i("LCD", "切换到页面 %d", current_page + 1);
// 播放页面切换提示音
MP3_Play(PARAM_SAVE_OK); // 使用参数保存成功的音效作为页面切换提示
}
/**
* @brief 切换到上一个显示页面
* @retval None
*/
void LCD_PrevPage(void) {
if (current_page == 0) {
current_page = 4;
} else {
current_page--;
}
elog_i("LCD", "切换到页面 %d", current_page + 1);
// 播放页面切换提示音
MP3_Play(PARAM_SAVE_OK);
}
/**
* @brief 设置当前显示页面
* @param page: 目标页面索引 (0-4)
* @retval None
*/
void LCD_SetPage(LCD_Page_t page) {
if (page < 5) {
current_page = page;
elog_i("LCD", "设置页面为 %d", page + 1);
MP3_Play(PARAM_SAVE_OK);
}
}
/**
* @brief 获取当前页面索引
* @retval 当前页面索引
*/
LCD_Page_t LCD_GetCurrentPage(void) { return current_page; }
/**
* @brief 更新传感器数据
* @param temp: 温度值
* @param humi: 湿度值
* @param weight: 食物重量
* @param water: 水位状态 (0=无水, 1=有水)
* @param mode: 系统模式
* @retval None
*/
void LCD_UpdateSensorData(float temp, float humi, float weight, uint8_t water,
uint8_t mode) {
sensor_data.temperature = temp;
sensor_data.humidity = humi;
sensor_data.food_weight = weight;
sensor_data.water_level = water;
sensor_data.system_mode = mode;
elog_i("LCD", "传感器数据更新: T=%.1fC H=%.1f%% W=%.1fg Water=%s Mode=%s",
temp, humi, weight, water ? "DETECTED" : "NONE",
mode ? "AUTO" : "MANUAL");
}
/**
* @brief 获取传感器数据指针
* @retval 传感器数据结构体指针
*/
Sensor_Data_t *LCD_GetSensorData(void) { return &sensor_data; }
/* USER CODE END LCD_Page_Functions */
/* USER CODE END Application */ /* USER CODE END Application */

View File

@@ -60,10 +60,10 @@ void MX_GPIO_Init(void)
/*Configure GPIO pin Output Level */ /*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, LED2_Pin|LED1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, LED2_Pin|LED1_Pin, GPIO_PIN_SET);
/*Configure GPIO pins : KEY1_Pin KEY2_Pin KEY3_Pin KEY3C3_Pin */ /*Configure GPIO pins : KEY1_Pin KEY2_Pin KEY3_Pin KEY4_Pin */
GPIO_InitStruct.Pin = KEY1_Pin|KEY2_Pin|KEY3_Pin|KEY3C3_Pin; GPIO_InitStruct.Pin = KEY1_Pin|KEY2_Pin|KEY3_Pin|KEY4_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pin : HX711_DOUT_Pin */ /*Configure GPIO pin : HX711_DOUT_Pin */

View File

@@ -45,7 +45,7 @@ void MX_SPI1_Init(void)
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT; hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE; hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;

View File

@@ -36,4 +36,43 @@
- `WIFI_Get_Current_Time()`:获取存储的当前时间(返回结构体副本) - `WIFI_Get_Current_Time()`:获取存储的当前时间(返回结构体副本)
- `WIFI_Is_Time_Valid()`:检查时间是否有效 - `WIFI_Is_Time_Valid()`:检查时间是否有效
### 使用示例: ## MP3 音频播放功能
本工程集成了 MP3 音频播放功能,用于提供各种系统状态和事件的语音提示。相关的音频索引定义在 `Core/Bsp/BSP_Device/bsp_mp3/mp3_play_index.h` 文件中。
### 音频提示分类:
#### 系统状态与初始化
- `SYS_POWER_ON` (1): 系统上电完成,智能宠物喂食系统启动完成,进入待机模式
- `WIFI_CONNECT_OK` (2): WiFi连接成功云平台数据同步已开启
- `WIFI_CONNECT_FAIL` (3): WiFi连接失败请检查网络配置
#### 喂食模块
- `FEED_AUTO_START` (4): 自动喂食启动,步进电机开始出粮
- `FEED_IN_PROGRESS` (5): 正在出粮,称重模块实时监测中
- `FEED_COMPLETE` (6): 喂食完成,当前食物重量已达设定值
- `FEED_MANUAL_TRIGGER` (7): 手动喂食指令已接收,开始出粮
- `FOOD_LOW_ALARM` (8): 食物余量不足警告,请及时添加
#### 喂水模块
- `WATER_REFILL_START` (9): 水位低于阈值,水泵启动,开始自动补水
- `WATER_REFILL_DONE` (10): 补水完成,水位已达设定上限
- `WATER_LOW_ALARM` (11): 水位过低警告,请检查水源或水泵
- `WATER_PIR_REFILL` (12): 检测到宠物靠近且水位偏低,启动自动补水
#### 模式切换
- `MODE_AUTO` (13): 已切换至自动运行模式
- `MODE_MANUAL` (14): 已切换至手动控制模式
#### 参数设置
- `PARAM_SAVE_OK` (15): 参数设置已保存,系统配置已更新
- `PARAM_TIME_SET` (16): 自动喂食时间已更新
- `PARAM_WEIGHT_SET` (17): 喂食重量阈值已更新
#### 远程控制与异常
- `REMOTE_CMD_RECEIVED` (18): 接收到微信小程序远程控制指令
- `DATA_UPLOAD_FAIL` (19): 数据上传失败,请检查网络连接
- `SYS_ERROR_ALARM` (20): 系统检测到异常,请检查硬件模块
### 使用说明:
系统通过调用相应的音频索引值来播放对应的提示音,为用户提供直观的听觉反馈,增强用户体验。

View File

@@ -35,7 +35,7 @@ Dma.USART1_TX.0.Priority=DMA_PRIORITY_VERY_HIGH
Dma.USART1_TX.0.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority Dma.USART1_TX.0.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority
FREERTOS.FootprintOK=true FREERTOS.FootprintOK=true
FREERTOS.IPParameters=Tasks01,FootprintOK,configTOTAL_HEAP_SIZE FREERTOS.IPParameters=Tasks01,FootprintOK,configTOTAL_HEAP_SIZE
FREERTOS.Tasks01=defaultTask,41,256,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL;wifi_mqtt,40,3000,wifi_task_mqtt,As external,NULL,Dynamic,NULL,NULL;LCD_SHOW_Task,40,1024,LCD_Task,Default,NULL,Dynamic,NULL,NULL FREERTOS.Tasks01=defaultTask,41,256,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL;wifi_mqtt,40,3000,wifi_task_mqtt,As external,NULL,Dynamic,NULL,NULL;LCD_SHOW_Task,40,1024,LCD_Task,Default,NULL,Dynamic,NULL,NULL;button,50,512,button_task,Default,NULL,Dynamic,NULL,NULL
FREERTOS.configTOTAL_HEAP_SIZE=30000 FREERTOS.configTOTAL_HEAP_SIZE=30000
File.Version=6 File.Version=6
GPIO.groupedBy=Group By Peripherals GPIO.groupedBy=Group By Peripherals
@@ -177,12 +177,14 @@ PB6.Mode=I2C
PB6.Signal=I2C1_SCL PB6.Signal=I2C1_SCL
PB7.Mode=I2C PB7.Mode=I2C
PB7.Signal=I2C1_SDA PB7.Signal=I2C1_SDA
PC0.GPIOParameters=GPIO_Label PC0.GPIOParameters=GPIO_PuPd,GPIO_Label
PC0.GPIO_Label=KEY1 PC0.GPIO_Label=KEY1
PC0.GPIO_PuPd=GPIO_PULLUP
PC0.Locked=true PC0.Locked=true
PC0.Signal=GPIO_Input PC0.Signal=GPIO_Input
PC1.GPIOParameters=GPIO_Label PC1.GPIOParameters=GPIO_PuPd,GPIO_Label
PC1.GPIO_Label=KEY2 PC1.GPIO_Label=KEY2
PC1.GPIO_PuPd=GPIO_PULLUP
PC1.Locked=true PC1.Locked=true
PC1.Signal=GPIO_Input PC1.Signal=GPIO_Input
PC12.Mode=Asynchronous PC12.Mode=Asynchronous
@@ -191,12 +193,14 @@ PC14-OSC32_IN.Mode=LSE-External-Oscillator
PC14-OSC32_IN.Signal=RCC_OSC32_IN PC14-OSC32_IN.Signal=RCC_OSC32_IN
PC15-OSC32_OUT.Mode=LSE-External-Oscillator PC15-OSC32_OUT.Mode=LSE-External-Oscillator
PC15-OSC32_OUT.Signal=RCC_OSC32_OUT PC15-OSC32_OUT.Signal=RCC_OSC32_OUT
PC2.GPIOParameters=GPIO_Label PC2.GPIOParameters=GPIO_PuPd,GPIO_Label
PC2.GPIO_Label=KEY3 PC2.GPIO_Label=KEY3
PC2.GPIO_PuPd=GPIO_PULLUP
PC2.Locked=true PC2.Locked=true
PC2.Signal=GPIO_Input PC2.Signal=GPIO_Input
PC3.GPIOParameters=GPIO_Label PC3.GPIOParameters=GPIO_PuPd,GPIO_Label
PC3.GPIO_Label=KEY3 PC3.GPIO_Label=KEY4
PC3.GPIO_PuPd=GPIO_PULLUP
PC3.Locked=true PC3.Locked=true
PC3.Signal=GPIO_Input PC3.Signal=GPIO_Input
PC4.GPIOParameters=GPIO_Label PC4.GPIOParameters=GPIO_Label
@@ -291,8 +295,8 @@ RCC.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK
RCC.TimSysFreq_Value=72000000 RCC.TimSysFreq_Value=72000000
RCC.USBFreq_Value=72000000 RCC.USBFreq_Value=72000000
RCC.VCOOutput2Freq_Value=8000000 RCC.VCOOutput2Freq_Value=8000000
SPI1.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_8 SPI1.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_4
SPI1.CalculateBaudRate=9.0 MBits/s SPI1.CalculateBaudRate=18.0 MBits/s
SPI1.Direction=SPI_DIRECTION_2LINES SPI1.Direction=SPI_DIRECTION_2LINES
SPI1.IPParameters=VirtualType,Mode,Direction,CalculateBaudRate,BaudRatePrescaler SPI1.IPParameters=VirtualType,Mode,Direction,CalculateBaudRate,BaudRatePrescaler
SPI1.Mode=SPI_MODE_MASTER SPI1.Mode=SPI_MODE_MASTER