编程技术分享

分享编程知识,探讨技术创新

0%

简介:基于CW32F030C8T6的嵌入式系统框架**

关注微信公众号,提前获取相关推文

本项目旨在构建一个通用的嵌入式系统框架,该框架将涵盖从硬件抽象层(HAL)到应用层的完整软件架构。我们将实现以下核心功能:

  1. **硬件抽象层 (HAL)**:提供对CW32F030C8T6芯片硬件资源的抽象访问接口,包括GPIO、UART、定时器、ADC等。
  2. **板级支持包 (BSP)**:针对立创·地文星CW32F030C8T6开发板,提供更高级别的硬件操作函数,例如LED控制、按键检测等。
  3. 设备驱动层:可以扩展添加各种外围设备驱动,例如传感器驱动、显示驱动等。
  4. **中间件层 (可选)**:可以根据项目需求添加中间件,例如RTOS、文件系统、网络协议栈等。在本项目中,为了简化,我们先不引入RTOS,但会设计成易于移植RTOS的架构。
  5. 应用层:实现具体的应用逻辑,例如数据采集、处理、通信等。
  6. 测试与验证框架:提供基本的单元测试和集成测试框架,确保系统可靠性。

代码设计架构:分层架构

我们将采用经典的分层架构来设计我们的嵌入式系统,这种架构具有良好的模块化、可维护性和可扩展性。

1
2
3
4
5
6
7
8
9
10
11
12
13
+---------------------+
| 应用层 (APP) | - 实现具体的应用逻辑
+---------------------+
| 中间件层 (MW) | - 可选层,提供通用服务 (例如 RTOS, 文件系统, 网络协议栈)
+---------------------+
| 设备驱动层 (DRV) | - 外围设备驱动,例如传感器,显示屏
+---------------------+
| 板级支持包 (BSP) | - 针对特定开发板的硬件操作函数
+---------------------+
| 硬件抽象层 (HAL) | - 芯片硬件资源的抽象访问接口
+---------------------+
| 硬件 (MCU) | - CW32F030C8T6 微控制器
+---------------------+

各层职责和设计原则:

  • **硬件抽象层 (HAL)**:
    • 职责:直接操作CW32F030C8T6的寄存器,提供最底层的硬件访问接口。
    • 设计原则
      • 硬件无关性:HAL层接口设计应尽可能通用,不依赖于具体的应用逻辑。
      • 封装性:将底层硬件操作细节封装在HAL层内部,上层模块无需关心底层硬件细节。
      • 效率:HAL层代码应尽可能高效,避免不必要的开销。
  • **板级支持包 (BSP)**:
    • 职责:针对特定的开发板硬件(例如LED、按键、特定接口),提供更高级别的硬件操作函数。BSP层基于HAL层构建。
    • 设计原则
      • 板级特定性:BSP层代码是针对特定开发板的,如果更换开发板,BSP层可能需要修改。
      • 易用性:BSP层接口应易于使用,方便应用层调用。
  • **设备驱动层 (DRV)**:
    • 职责:驱动各种外围设备,例如传感器、显示屏、通信模块等。驱动层基于HAL或BSP层构建。
    • 设计原则
      • 设备特定性:驱动层代码是针对特定设备的,如果更换设备,驱动层需要修改或替换。
      • 可重用性:驱动层代码应尽可能通用,方便在不同项目中重用。
      • 模块化:每个设备驱动应独立成模块,方便管理和维护。
  • **中间件层 (MW)**:
    • 职责:提供通用的软件服务,例如实时操作系统 (RTOS)、文件系统、网络协议栈、图形库等。中间件层基于HAL、BSP或驱动层构建。
    • 设计原则
      • 通用性:中间件层提供通用的服务,可以在多个应用中使用。
      • 可配置性:中间件层应具有良好的可配置性,以适应不同的应用需求。
      • 性能:中间件层应考虑性能,避免引入不必要的开销。
  • **应用层 (APP)**:
    • 职责:实现具体的应用逻辑,例如数据采集、处理、控制、通信、用户界面等。应用层基于中间件层、驱动层、BSP层或HAL层构建。
    • 设计原则
      • 应用特定性:应用层代码是针对特定应用的,根据应用需求进行设计。
      • 清晰性:应用层代码应结构清晰,易于理解和维护。
      • 效率:应用层代码应考虑效率,满足应用的性能需求。

C 代码实现 (示例代码,这里仅提供核心框架和部分模块的详细代码,您可以根据实际情况扩展和完善)

1. 项目目录结构:

为了更好地组织代码,我们采用如下目录结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
├── Core                # 核心代码
│ ├── Inc # 核心头文件
│ │ ├── hal.h # HAL层头文件
│ │ ├── bsp.h # BSP层头文件
│ │ ├── drv.h # 设备驱动层头文件 (如果需要)
│ │ ├── mw.h # 中间件层头文件 (如果需要)
│ │ └── app.h # 应用层头文件
│ └── Src # 核心源文件
│ ├── hal.c # HAL层源文件
│ ├── bsp.c # BSP层源文件
│ ├── drv # 设备驱动层源文件目录 (如果需要)
│ │ └── ...
│ ├── mw # 中间件层源文件目录 (如果需要)
│ │ └── ...
│ └── app # 应用层源文件目录
│ └── main.c # 主应用程序入口
├── Drivers # CW32F030 官方驱动库 (CMSIS 和 HAL 库)
│ ├── CW32F030_HAL_Driver # CW32F030 HAL 驱动库
│ │ ├── Inc
│ │ └── Src
│ └── Device # CMSIS 设备描述
│ └── CW32F030
│ ├── Include
│ └── Source
├── Example # 示例代码和配置
│ ├── Inc # 示例头文件
│ └── Src # 示例源文件
├── Lib # 外部库 (例如 RTOS, 文件系统, 网络协议栈,如果需要)
│ └── ...
├── Middlewares # 中间件源代码 (如果需要)
│ └── ...
├── Project # 项目文件 (例如 Keil, IAR, GCC 项目文件)
│ └── ...
├── Startup # 启动代码
│ └── ...
└── Utilities # 实用工具代码 (例如调试打印,延时函数)
├── Inc
└── Src

2. 硬件抽象层 (HAL) 代码示例 (hal.h 和 hal.c):

hal.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#ifndef __HAL_H__
#define __HAL_H__

#include "CW32F030.h" // 包含 CW32F030 头文件

// GPIO 相关定义
typedef enum {
GPIO_MODE_INPUT,
GPIO_MODE_OUTPUT,
GPIO_MODE_AF, // Alternate Function
GPIO_MODE_ANALOG
} GPIO_ModeTypeDef;

typedef enum {
GPIO_SPEED_LOW,
GPIO_SPEED_MEDIUM,
GPIO_SPEED_HIGH
} GPIO_SpeedTypeDef;

typedef enum {
GPIO_PULL_NONE,
GPIO_PULLUP,
GPIO_PULLDOWN
} GPIO_PullTypeDef;

typedef enum {
GPIO_PIN_RESET = 0,
GPIO_PIN_SET = 1
} GPIO_PinState;

typedef struct {
GPIO_TypeDef* GPIOx; // GPIO 端口 (GPIOA, GPIOB, ...)
uint16_t Pin; // GPIO 引脚 (GPIO_PIN_0, GPIO_PIN_1, ...)
GPIO_ModeTypeDef Mode; // GPIO 模式
GPIO_SpeedTypeDef Speed; // GPIO 速度
GPIO_PullTypeDef Pull; // 上拉/下拉
uint8_t Alternate; // 复用功能 (如果 Mode 为 GPIO_MODE_AF)
} GPIO_InitTypeDef;

// GPIO 初始化函数
void HAL_GPIO_Init(GPIO_InitTypeDef *GPIO_InitStruct);

// GPIO 引脚输出高/低电平
void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);

// GPIO 引脚读取输入电平
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

// GPIO 引脚电平翻转
void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);


// UART 相关定义和函数 (可以根据需要添加更多 UART 配置,例如波特率,数据位,校验位等)
typedef struct {
USART_TypeDef* USARTx; // UART 端口 (USART0, USART1, ...)
uint32_t BaudRate; // 波特率
uint32_t WordLength; // 数据位长度 (例如 8 位)
uint32_t StopBits; // 停止位 (例如 1 位)
uint32_t Parity; // 校验位 (例如 无校验)
uint32_t Mode; // 模式 (例如 发送/接收)
uint32_t HwFlowCtl; // 硬件流控 (例如 无流控)
uint32_t OverSampling; // 过采样 (例如 16 倍过采样)
} UART_InitTypeDef;

// UART 初始化函数
void HAL_UART_Init(UART_InitTypeDef *UART_InitStruct);

// UART 发送一个字节
void HAL_UART_Transmit(USART_TypeDef* USARTx, uint8_t data);

// UART 接收一个字节 (阻塞方式)
uint8_t HAL_UART_Receive(USART_TypeDef* USARTx);

// UART 发送字符串
void HAL_UART_TransmitString(USART_TypeDef* USARTx, const char *str);


// 时钟初始化 (示例,根据CW32F030时钟系统具体配置)
void HAL_RCC_SystemClock_Config(void);

// 系统延时函数 (使用 SysTick 或 定时器实现)
void HAL_Delay(uint32_t Delay);

#endif /* __HAL_H__ */

hal.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#include "hal.h"

// ----- GPIO HAL 函数实现 -----

void HAL_GPIO_Init(GPIO_InitTypeDef *GPIO_InitStruct) {
// 使能 GPIO 时钟 (根据 GPIO 端口选择相应的时钟)
if (GPIO_InitStruct->GPIOx == GPIOA) {
RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_GPIOA, ENABLE);
} else if (GPIO_InitStruct->GPIOx == GPIOB) {
RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_GPIOB, ENABLE);
} // ... 可以添加更多 GPIO 端口的时钟使能

uint32_t mode = 0x00;
uint32_t pull = 0x00;
uint32_t speed = 0x00;
uint32_t af = 0x00;

// 配置 GPIO 模式
switch (GPIO_InitStruct->Mode) {
case GPIO_MODE_INPUT:
mode = 0x00; // 输入模式
break;
case GPIO_MODE_OUTPUT:
mode = 0x01; // 输出模式
break;
case GPIO_MODE_AF:
mode = 0x02; // 复用功能模式
af = GPIO_InitStruct->Alternate; // 设置复用功能
break;
case GPIO_MODE_ANALOG:
mode = 0x03; // 模拟模式
break;
default:
break;
}
GPIO_InitStruct->GPIOx->CFG &= ~(3 << (GPIO_InitStruct->Pin * 2)); // 清除模式配置位
GPIO_InitStruct->GPIOx->CFG |= (mode << (GPIO_InitStruct->Pin * 2)); // 设置模式

// 配置 GPIO 上拉/下拉
switch (GPIO_InitStruct->Pull) {
case GPIO_PULLUP:
pull = 0x01; // 上拉
break;
case GPIO_PULLDOWN:
pull = 0x02; // 下拉
break;
case GPIO_PULL_NONE:
default:
pull = 0x00; // 无上拉/下拉
break;
}
GPIO_InitStruct->GPIOx->PUPD &= ~(3 << (GPIO_InitStruct->Pin * 2)); // 清除上拉/下拉配置位
GPIO_InitStruct->GPIOx->PUPD |= (pull << (GPIO_InitStruct->Pin * 2)); // 设置上拉/下拉

// 配置 GPIO 输出速度 (根据 CW32F030 数据手册配置速度寄存器,这里简化)
switch (GPIO_InitStruct->Speed) {
case GPIO_SPEED_HIGH:
speed = 0x03; // 高速
break;
case GPIO_SPEED_MEDIUM:
speed = 0x02; // 中速
break;
case GPIO_SPEED_LOW:
default:
speed = 0x01; // 低速
break;
}
// CW32F030 没有明确的 GPIO 速度配置位,这里可以根据实际情况进行配置,例如驱动能力等

// 配置复用功能 (如果需要)
if (GPIO_InitStruct->Mode == GPIO_MODE_AF) {
// 配置 GPIO 复用功能寄存器 (根据 CW32F030 数据手册配置 AFSEL 寄存器)
// 例如:GPIO_InitStruct->GPIOx->AFSEL[GPIO_InitStruct->Pin >> 3] &= ~(0xF << ((GPIO_InitStruct->Pin & 0x07) * 4));
// GPIO_InitStruct->GPIOx->AFSEL[GPIO_InitStruct->Pin >> 3] |= (af << ((GPIO_InitStruct->Pin & 0x07) * 4));
// 具体配置请参考 CW32F030 数据手册
}
}

void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState) {
if (PinState != GPIO_PIN_RESET) {
GPIOx->PDIR |= GPIO_Pin; // 设置引脚为高电平
} else {
GPIOx->PDIR &= ~GPIO_Pin; // 设置引脚为低电平
}
}

GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) {
if ((GPIOx->PDIR & GPIO_Pin) != 0x00) {
return GPIO_PIN_SET;
} else {
return GPIO_PIN_RESET;
}
}

void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) {
GPIOx->PODR ^= GPIO_Pin; // 翻转引脚电平
}


// ----- UART HAL 函数实现 -----

void HAL_UART_Init(UART_InitTypeDef *UART_InitStruct) {
// 使能 UART 时钟 (根据 UART 端口选择相应的时钟)
if (UART_InitStruct->USARTx == USART0) {
RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_USART0, ENABLE);
} else if (UART_InitStruct->USARTx == USART1) {
RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_USART1, ENABLE);
} // ... 可以添加更多 UART 端口的时钟使能

// 配置 UART 波特率
USART_InitStruct->USARTx->BRR = SystemCoreClock / UART_InitStruct->BaudRate; // 假设使用系统时钟作为 UART 时钟源

// 配置 UART 数据位长度, 停止位, 校验位, 模式, 硬件流控, 过采样 (根据 UART_InitStruct 配置寄存器)
// ... 具体配置请参考 CW32F030 数据手册

// 使能 UART 发送和接收 (如果需要)
if (UART_InitStruct->Mode & USART_MODE_TX) {
USART_InitStruct->USARTx->CTL |= USART_CTL_TE; // 使能发送器
}
if (UART_InitStruct->Mode & USART_MODE_RX) {
USART_InitStruct->USARTx->CTL |= USART_CTL_RE; // 使能接收器
}

// 使能 UART
USART_InitStruct->USARTx->CTL |= USART_CTL_UE; // 使能 UART
}

void HAL_UART_Transmit(USART_TypeDef* USARTx, uint8_t data) {
// 等待发送缓冲区空
while (!(USARTx->ISR & USART_ISR_TXE));
USARTx->TDR = data; // 发送数据
}

uint8_t HAL_UART_Receive(USART_TypeDef* USARTx) {
// 等待接收数据就绪
while (!(USARTx->ISR & USART_ISR_RXNE));
return (uint8_t)USARTx->RDR; // 接收数据
}

void HAL_UART_TransmitString(USART_TypeDef* USARTx, const char *str) {
while (*str) {
HAL_UART_Transmit(USARTx, *str++);
}
}


// ----- 时钟配置 HAL 函数实现 -----

void HAL_RCC_SystemClock_Config(void) {
// 这里示例配置使用 HSI 内部高速时钟作为系统时钟
RCC_HSI_Enable(RCC_HSIOSC_DIV1); // 使能 HSI,不分频
while (RCC_GetHSIStableFlag() != SET); // 等待 HSI 稳定
RCC_SYSCLKConfig(RCC_SYSCLKSource_HSI); // 选择 HSI 作为系统时钟源

// 设置 AHB, APB1, APB2 总线时钟分频系数 (根据需要配置)
// 例如:RCC_HCLKConfig(RCC_SYSCLK_Div1); // AHB 时钟不分频
// RCC_PCLK1Config(RCC_HCLK_Div1); // APB1 时钟不分频
// RCC_PCLK2Config(RCC_HCLK_Div1); // APB2 时钟不分频

SystemCoreClockUpdate(); // 更新系统时钟频率变量 SystemCoreClock
}


// ----- 延时 HAL 函数实现 (使用 SysTick) -----

static __IO uint32_t uwTick; // 嘀嗒定时器计数器

void HAL_Delay(uint32_t Delay) {
uint32_t tickstart = uwTick;
uint32_t wait = Delay;

/* Add a freq to guarantee minimum wait */
if (wait < HAL_MAX_DELAY) {
wait += (uint32_t)(1);
}

while((uwTick - tickstart) < wait) {
;
}
}

// SysTick 中断处理函数 (需要在启动代码中配置 SysTick 并使能中断)
void SysTick_Handler(void) {
uwTick++; // 嘀嗒计数器自增
// 可以添加其他 SysTick 中断处理逻辑
}

// 初始化 SysTick 定时器 (在系统初始化时调用)
void HAL_InitTick(uint32_t TickPriority) {
// 配置 SysTick 定时器,例如配置 1ms 中断一次
SysTick_Config(SystemCoreClock / 1000); // 假设 SystemCoreClock 是系统时钟频率
// 设置 SysTick 中断优先级 (可选)
// NVIC_SetPriority(SysTick_IRQn, TickPriority);
}


// 初始化 HAL 层 (在系统初始化时调用)
void HAL_Init(void) {
HAL_RCC_SystemClock_Config(); // 配置系统时钟
HAL_InitTick(0); // 初始化 SysTick 定时器
}

3. 板级支持包 (BSP) 代码示例 (bsp.h 和 bsp.c):

bsp.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#ifndef __BSP_H__
#define __BSP_H__

#include "hal.h"

// LED 相关定义
#define LED1_PIN GPIO_PIN_0 // 假设 LED1 连接到 GPIOA_PIN_0
#define LED1_GPIO_PORT GPIOA

// 按键相关定义
#define KEY1_PIN GPIO_PIN_1 // 假设 KEY1 连接到 GPIOA_PIN_1
#define KEY1_GPIO_PORT GPIOA

// 初始化 LED
void BSP_LED_Init(void);

// 控制 LED 亮/灭
void BSP_LED_Control(uint8_t led_num, GPIO_PinState state); // led_num: 1, 2, ...

// 初始化 按键
void BSP_KEY_Init(void);

// 读取按键状态
GPIO_PinState BSP_KEY_GetState(uint8_t key_num); // key_num: 1, 2, ...

// 初始化 UART 用于调试打印
void BSP_DEBUG_UART_Init(uint32_t baudrate);

// 调试打印函数
void BSP_DEBUG_Print(const char *fmt, ...);

#endif /* __BSP_H__ */

bsp.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include "bsp.h"
#include <stdio.h>
#include <stdarg.h>

// ----- LED BSP 函数实现 -----

void BSP_LED_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};

// 配置 LED1 引脚
GPIO_InitStruct.GPIOx = LED1_GPIO_PORT;
GPIO_InitStruct.Pin = LED1_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
GPIO_InitStruct.Pull = GPIO_PULL_NONE;
HAL_GPIO_Init(&GPIO_InitStruct);

// 默认关闭 LED
BSP_LED_Control(1, GPIO_PIN_RESET);
}

void BSP_LED_Control(uint8_t led_num, GPIO_PinState state) {
if (led_num == 1) {
HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_PIN, state);
} // ... 可以添加更多 LED 控制
}


// ----- 按键 BSP 函数实现 -----

void BSP_KEY_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};

// 配置 KEY1 引脚
GPIO_InitStruct.GPIOx = KEY1_GPIO_PORT;
GPIO_InitStruct.Pin = KEY1_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
GPIO_InitStruct.Pull = GPIO_PULLUP; // 默认上拉,按下时为低电平
HAL_GPIO_Init(&GPIO_InitStruct);
}

GPIO_PinState BSP_KEY_GetState(uint8_t key_num) {
if (key_num == 1) {
return HAL_GPIO_ReadPin(KEY1_GPIO_PORT, KEY1_PIN);
} // ... 可以添加更多按键状态读取
return GPIO_PIN_SET; // 默认返回未按下状态
}


// ----- 调试 UART BSP 函数实现 -----

#define DEBUG_UART_PORT USART0 // 选择 USART0 作为调试串口
#define DEBUG_UART_TX_PIN GPIO_PIN_9 // 假设 TX 连接到 GPIOA_PIN_9
#define DEBUG_UART_RX_PIN GPIO_PIN_10 // 假设 RX 连接到 GPIOA_PIN_10
#define DEBUG_UART_GPIO_PORT GPIOA

void BSP_DEBUG_UART_Init(uint32_t baudrate) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
UART_InitTypeDef UART_InitStruct = {0};

// 配置 UART TX 引脚复用功能
GPIO_InitStruct.GPIOx = DEBUG_UART_GPIO_PORT;
GPIO_InitStruct.Pin = DEBUG_UART_TX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Alternate = 1; // 根据 CW32F030 数据手册配置 UART TX 复用功能
HAL_GPIO_Init(&GPIO_InitStruct);

// 配置 UART RX 引脚复用功能
GPIO_InitStruct.GPIOx = DEBUG_UART_GPIO_PORT;
GPIO_InitStruct.Pin = DEBUG_UART_RX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF;
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Alternate = 1; // 根据 CW32F030 数据手册配置 UART RX 复用功能
HAL_GPIO_Init(&GPIO_InitStruct);

// 配置 UART 模块
UART_InitStruct.USARTx = DEBUG_UART_PORT;
UART_InitStruct.BaudRate = baudrate;
UART_InitStruct.WordLength = USART_WORDLENGTH_8B;
UART_InitStruct.StopBits = USART_STOPBITS_1;
UART_InitStruct.Parity = USART_PARITY_NONE;
UART_InitStruct.Mode = USART_MODE_TX_RX; // 使能发送和接收
UART_InitStruct.HwFlowCtl = USART_HWCONTROL_NONE;
UART_InitStruct.OverSampling = USART_OVERSAMPLING_16;
HAL_UART_Init(&UART_InitStruct);
}


void BSP_DEBUG_Print(const char *fmt, ...) {
char buffer[256]; // 缓冲区大小可以根据需要调整
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args); // 格式化字符串到缓冲区
va_end(args);

HAL_UART_TransmitString(DEBUG_UART_PORT, buffer); // 通过 UART 发送字符串
}

4. 应用层代码示例 (app/main.c):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include "app.h"
#include "bsp.h"
#include "hal.h"
#include "stdio.h"

int main(void) {
HAL_Init(); // 初始化 HAL 层 (时钟, SysTick)
BSP_LED_Init(); // 初始化 LED
BSP_KEY_Init(); // 初始化 按键
BSP_DEBUG_UART_Init(115200); // 初始化调试串口

BSP_DEBUG_Print("System Start!\r\n");

while (1) {
// LED 闪烁示例
BSP_LED_Control(1, GPIO_PIN_SET); // 点亮 LED1
HAL_Delay(500); // 延时 500ms
BSP_LED_Control(1, GPIO_PIN_RESET); // 关闭 LED1
HAL_Delay(500); // 延时 500ms

// 按键检测示例
if (BSP_KEY_GetState(1) == GPIO_PIN_RESET) { // 按键按下 (低电平有效)
BSP_DEBUG_Print("Key 1 Pressed!\r\n");
BSP_LED_Control(1, GPIO_PIN_SET); // 按下按键时点亮 LED
HAL_Delay(200); // 短暂延时去抖动
} else {
BSP_LED_Control(1, GPIO_PIN_RESET); // 松开按键时关闭 LED
}
}
}

5. startup 代码和项目配置:

您需要根据您使用的IDE (Keil, IAR, GCC 等) 创建相应的项目,并配置启动代码、链接脚本、芯片型号等。CW32F030 的启动代码和库文件通常由芯片厂商提供,您可以从立创或者中微半导体的官方网站下载 CW32F030 的开发资料和 SDK 包,里面会包含完整的例程和项目模板。

测试与验证框架:

为了确保代码的可靠性,我们应该建立基本的测试框架。

  • 单元测试:针对 HAL 层和 BSP 层中的每个模块和函数进行单元测试,例如测试 GPIO 的输入输出功能,UART 的发送接收功能等。可以使用一些简单的测试用例来验证函数的正确性。
  • 集成测试:将各个模块组合起来进行集成测试,例如测试 LED 和按键的联动功能,UART 通信功能等。
  • 系统测试:对整个系统进行功能测试和性能测试,验证系统是否满足需求。

维护与升级:

  • 模块化设计:分层架构和模块化设计使得系统易于维护和升级。当需要修改或添加功能时,只需要修改或添加相应的模块,而不会影响其他模块。
  • 版本控制:使用版本控制系统 (例如 Git) 来管理代码,方便代码的版本管理、回溯和协作开发。
  • 文档化:编写清晰的代码注释和文档,方便代码的理解和维护。
  • 固件升级机制:设计可靠的固件升级机制,方便在产品发布后进行 bug 修复和功能升级。可以使用 UART、USB 或 OTA (Over-The-Air) 等方式进行固件升级。

总结:

以上代码示例和架构设计提供了一个基于 CW32F030C8T6 开发板的嵌入式系统框架的基础。这个框架是分层的、模块化的,易于扩展和维护。您可以根据实际项目需求,在这个框架上添加设备驱动层、中间件层和更复杂的应用逻辑。

您可以进行以下扩展和完善:

  1. 完善 HAL 层
    • 实现 CW32F030 所有外设的 HAL 驱动,例如 ADC、Timer、SPI、I2C、CAN、RTC、Watchdog 等。
    • 为每个外设的 HAL 驱动添加详细的配置结构体和 API 函数,并编写详细的注释和示例代码。
    • 添加错误处理机制,例如 HAL 错误码定义和错误处理函数。
  2. 扩展 BSP 层
    • 针对立创·地文星 CW32F030C8T6 开发板上的各种板载资源 (例如 LED 灯阵、OLED 显示屏、传感器、扩展接口等) 提供 BSP 驱动。
    • 编写更丰富的 BSP 例程,例如 LED 流水灯、按键扫描、OLED 显示字符/图片、传感器数据采集等。
  3. **添加设备驱动层 (DRV)**:
    • 选择一些常用的外围设备 (例如温湿度传感器、光照传感器、加速度传感器、陀螺仪、LCD 显示屏、触摸屏、无线通信模块等),编写相应的设备驱动程序。
    • 每个设备驱动程序应包含初始化、读写数据、控制等 API 函数,并提供详细的示例代码。
    • 考虑驱动程序的通用性和可重用性,设计良好的驱动接口。
  4. **引入中间件层 (MW)**:
    • 移植一个轻量级的 RTOS (例如 FreeRTOS、RT-Thread Nano 等) 到 CW32F030 平台,并编写 RTOS 的 BSP 适配层。
    • 添加常用的中间件库,例如文件系统 (FatFS)、网络协议栈 (lwIP)、图形库 (emWin) 等,并编写相应的移植和适配代码。
    • 编写使用中间件的示例代码,例如基于 RTOS 的多任务应用、基于 FatFS 的文件读写、基于 lwIP 的网络通信等。
  5. **开发更复杂的应用层 (APP)**:
    • 基于上述 HAL、BSP、DRV 和 MW 层,开发更复杂的嵌入式应用,例如环境监测系统、智能家居控制系统、工业自动化控制系统等。
    • 设计清晰的应用层模块结构,实现应用的各种功能,例如数据采集、数据处理、数据存储、通信控制、用户交互等。
    • 编写详细的应用层代码,并进行充分的测试和验证。
  6. 完善测试与验证框架
    • 编写更全面的单元测试用例,覆盖 HAL 层和 BSP 层的各个模块和函数。
    • 设计集成测试和系统测试方案,验证系统功能的完整性和可靠性。
    • 使用自动化测试工具,提高测试效率和覆盖率。
  7. 编写详细的文档和注释
    • 为每个模块和函数编写详细的注释,解释代码的功能、用法和注意事项。
    • 编写项目开发文档,包括系统架构设计、模块功能描述、API 文档、开发指南、测试报告等。
    • 使用文档生成工具 (例如 Doxygen) 自动生成 API 文档。

通过以上扩展和完善,您可以构建一个功能完善的嵌入式系统框架,并充分展示您作为高级嵌入式软件开发工程师的技能和经验。 请记住,代码的质量和可维护性比代码行数更重要,编写清晰、规范、可读性强的代码才是关键。

欢迎关注我的其它发布渠道