编程技术分享

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

0%

简介:基于GD32(梁山派)设计的瓦力机器人,配备蓝牙,2.4G,Lora进行遥控通信,支持8路舵机控制,板载1.3寸ISP LCD屏,协配VC-02进行语音交互,摄像头模块支持150米实时图传。

好的,作为一名高级嵌入式软件开发工程师,我将为您详细阐述基于GD32(梁山派)设计的瓦力机器人的嵌入式软件架构,并提供相应的C代码实现。这个项目旨在构建一个可靠、高效、可扩展的嵌入式系统平台,涵盖需求分析、系统实现、测试验证和维护升级的完整开发流程。
关注微信公众号,提前获取相关推文

1. 系统架构设计

针对瓦力机器人的功能需求和硬件配置,我将采用分层架构结合模块化设计的思想来构建软件系统。这种架构具有良好的可维护性、可扩展性和可移植性,能够有效应对嵌入式系统开发的复杂性。

1.1 分层架构

系统架构将划分为以下几个层次,从下到上依次为:

  • 硬件抽象层 (HAL - Hardware Abstraction Layer): 直接与硬件交互,提供统一的硬件访问接口,屏蔽底层硬件的差异,方便上层模块的移植和复用。HAL层包括GPIO、定时器、UART、SPI、I2C、ADC、DAC等底层驱动。
  • 板级支持包 (BSP - Board Support Package): 针对具体的GD32开发板,配置系统时钟、外设初始化、中断管理等,为上层提供硬件平台的基础支持。BSP层依赖于HAL层。
  • 驱动层 (Drivers): 封装外围器件的驱动程序,例如舵机驱动、LCD驱动、摄像头驱动、语音交互模块驱动、无线通信模块驱动等。驱动层基于HAL层提供的接口进行开发。
  • 通信层 (Communication Layer): 处理各种通信协议,例如蓝牙、2.4G、Lora,实现遥控指令的接收和数据传输。通信层依赖于驱动层。
  • 应用层 (Application Layer): 实现机器人的核心功能逻辑,例如运动控制、姿态调整、语音交互、图像传输、自主导航(如果未来扩展)等。应用层调用驱动层和通信层提供的接口。
  • 操作系统层 (OS Layer): 选择合适的实时操作系统 (RTOS) 或裸机系统,进行任务调度、资源管理等,提高系统的实时性和并发处理能力。(本项目为了演示代码量,我们先从裸机系统开始,后续可以扩展到RTOS)

1.2 模块化设计

在每个层次内部,进一步进行模块化设计,将功能划分为独立的模块,例如:

  • HAL层模块: GPIO模块、Timer模块、UART模块、SPI模块、I2C模块、ADC模块、DAC模块等。
  • 驱动层模块: 舵机驱动模块、LCD驱动模块、摄像头驱动模块、语音交互模块驱动、蓝牙模块驱动、2.4G模块驱动、Lora模块驱动等。
  • 通信层模块: 蓝牙通信模块、2.4G通信模块、Lora通信模块、遥控指令解析模块等。
  • 应用层模块: 运动控制模块、舵机控制模块、LCD显示模块、语音交互模块、图像传输模块、状态管理模块、错误处理模块等。

1.3 架构图

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
+-------------------+  应用层 (Application Layer)  -------------------+
| 运动控制模块 | 舵机控制模块 | LCD显示模块 | 语音交互模块 | 图像传输模块 | ... |
+-------------------+--------------------+--------------------+--------------------+--------------------+-----+
| 通信层 (Communication Layer) |
+-----------------------+-------------------------+-------------------------+-----------------------+
| 蓝牙通信模块 | 2.4G通信模块 | Lora通信模块 | 遥控指令解析模块 |
+-----------------------+-------------------------+-------------------------+-----------------------+
| 驱动层 (Drivers) |
+-------------------+--------------------+--------------------+--------------------+--------------------+-----+
| 舵机驱动模块 | LCD驱动模块 | 摄像头驱动模块 | 语音交互模块驱动 | 蓝牙模块驱动 | ... |
| 2.4G模块驱动 | Lora模块驱动 | | | | |
+-------------------+--------------------+--------------------+--------------------+--------------------+-----+
| 板级支持包 (BSP) |
+-------------------------------------------------------------------------------------------------+
| 系统时钟配置 | 外设初始化 | 中断管理 | 启动代码 | ... |
+-------------------------------------------------------------------------------------------------+
| 硬件抽象层 (HAL) |
+-----------------------+-------------------------+-------------------------+-----------------------+
| GPIO驱动 | Timer驱动 | UART驱动 | SPI驱动 | ... |
| I2C驱动 | ADC驱动 | DAC驱动 | | |
+-----------------------+-------------------------+-------------------------+-----------------------+
| 硬件 (Hardware) |
+-------------------------------------------------------------------------------------------------+
| GD32微控制器 | 蓝牙模块 | 2.4G模块 | Lora模块 | 舵机 | LCD屏 | 摄像头 | 语音IC | ... |
+-------------------------------------------------------------------------------------------------+

2. 技术选型和方法

  • GD32微控制器: 选择GD32F系列高性能微控制器,例如GD32F303RCT6或更高级型号,具备丰富的片上资源和强大的运算能力,满足瓦力机器人的控制需求。
  • C语言编程: 采用C语言作为主要的开发语言,C语言在嵌入式系统开发中具有高效、灵活、可移植性强等优点。
  • 模块化编程: 将系统划分为独立的模块,提高代码的可读性、可维护性和可复用性。
  • 事件驱动编程: 对于异步事件,例如遥控指令接收、传感器数据采集等,采用事件驱动的方式进行处理,提高系统的响应速度和实时性。
  • 状态机设计: 使用状态机来管理机器人的不同工作状态,例如待机状态、运动状态、语音交互状态等,使系统逻辑清晰、易于控制。
  • 中断机制: 充分利用中断机制处理实时性要求高的事件,例如定时器中断、外部中断、串口接收中断等。
  • DMA (Direct Memory Access): 使用DMA技术进行数据传输,例如LCD数据传输、摄像头数据传输、串口数据传输等,减轻CPU的负担,提高数据传输效率。
  • 错误处理机制: 建立完善的错误处理机制,包括错误检测、错误报告、错误恢复等,提高系统的鲁棒性和可靠性。
  • 版本控制工具 (Git): 使用Git进行代码版本管理,方便团队协作、代码追踪和版本回溯。
  • 调试工具: 使用J-Link/ST-Link等调试器进行硬件调试,使用串口打印、在线调试等方法进行软件调试。

3. C代码实现

为了代码的组织性和可读性,我们将代码按照架构层次和模块进行组织。以下代码示例仅为核心模块的框架和关键代码片段,完整代码将远超3000行,实际项目中需要根据具体硬件和功能进行完善。

3.1 HAL层 (HAL - Hardware Abstraction Layer)

  • hal_gpio.h: GPIO驱动头文件
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
#ifndef HAL_GPIO_H
#define HAL_GPIO_H

#include "gd32f30x.h" // 根据具体的GD32型号选择头文件

typedef enum {
GPIO_MODE_INPUT,
GPIO_MODE_OUTPUT,
GPIO_MODE_AF_PP, // Alternate Function Push-Pull
GPIO_MODE_AF_OD // Alternate Function Open-Drain
} gpio_mode_t;

typedef enum {
GPIO_SPEED_LOW,
GPIO_SPEED_MEDIUM,
GPIO_SPEED_HIGH
} gpio_speed_t;

typedef enum {
GPIO_PUPD_NONE,
GPIO_PUPD_PULLUP,
GPIO_PUPD_PULLDOWN
} gpio_pupd_t;

void hal_gpio_init(uint32_t gpio_periph, uint16_t gpio_pin, gpio_mode_t mode, gpio_speed_t speed, gpio_pupd_t pupd);
void hal_gpio_write_pin(uint32_t gpio_periph, uint16_t gpio_pin, uint8_t value);
uint8_t hal_gpio_read_pin(uint32_t gpio_periph, uint16_t gpio_pin);

#endif // HAL_GPIO_H
  • hal_gpio.c: GPIO驱动源文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "hal_gpio.h"

void hal_gpio_init(uint32_t gpio_periph, uint16_t gpio_pin, gpio_mode_t mode, gpio_speed_t speed, gpio_pupd_t pupd) {
rcu_periph_clock_enable(gpio_periph); // 使能GPIO时钟

gpio_mode_set(GET_GPIO_PORT(gpio_periph), mode, pupd, gpio_pin);
gpio_output_options_set(GET_GPIO_PORT(gpio_periph), GPIO_OTYPE_PP, speed, gpio_pin); // 推挽输出,速度配置
}

void hal_gpio_write_pin(uint32_t gpio_periph, uint16_t gpio_pin, uint8_t value) {
if (value) {
gpio_bit_set(GET_GPIO_PORT(gpio_periph), gpio_pin);
} else {
gpio_bit_reset(GET_GPIO_PORT(gpio_periph), gpio_pin);
}
}

uint8_t hal_gpio_read_pin(uint32_t gpio_periph, uint16_t gpio_pin) {
return gpio_input_bit_get(GET_GPIO_PORT(gpio_periph), gpio_pin);
}
  • hal_timer.h: Timer驱动头文件
1
2
3
4
5
6
7
8
9
10
11
#ifndef HAL_TIMER_H
#define HAL_TIMER_H

#include "gd32f30x.h"

void hal_timer_init(uint32_t timer_periph, uint32_t prescaler, uint32_t period);
void hal_timer_start(uint32_t timer_periph);
void hal_timer_stop(uint32_t timer_periph);
void hal_timer_enable_irq(uint32_t timer_periph, uint8_t priority, void (*irq_handler)(void));

#endif // HAL_TIMER_H
  • hal_timer.c: Timer驱动源文件
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
#include "hal_timer.h"

void hal_timer_init(uint32_t timer_periph, uint32_t prescaler, uint32_t period) {
timer_parameter_struct timer_initpara;

rcu_periph_clock_enable(GET_TIMER_CLK(timer_periph)); // 使能定时器时钟

timer_deinit(timer_periph);

timer_struct_para_init(&timer_initpara);
timer_initpara.prescaler = prescaler;
timer_initpara.period = period;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.repetitioncounter = 0;
timer_timer_init(timer_periph, &timer_initpara);

timer_update_event_enable(timer_periph); // 使能更新事件
}

void hal_timer_start(uint32_t timer_periph) {
timer_enable(timer_periph);
}

void hal_timer_stop(uint32_t timer_periph) {
timer_disable(timer_periph);
}

void hal_timer_enable_irq(uint32_t timer_periph, uint8_t priority, void (*irq_handler)(void)) {
nvic_irq_enable(GET_TIMER_IRQN(timer_periph), priority, 0); // 使能定时器中断
timer_interrupt_enable(timer_periph, TIMER_INT_UP); // 使能更新中断
timer_irq_handler[GET_TIMER_INDEX(timer_periph)] = irq_handler; // 注册中断处理函数
}

// 定时器中断处理函数表,用于根据定时器索引调用对应的用户中断处理函数
void (*timer_irq_handler[TIMER_MAX_INDEX])(void) = {NULL};

// 通用定时器中断处理函数 (需要根据实际使用的定时器修改)
void TIMER2_IRQHandler(void) {
if (timer_interrupt_flag_get(TIMER2, TIMER_INT_FLAG_UP) != RESET) {
timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_UP);
if (timer_irq_handler[TIMER2_INDEX] != NULL) {
timer_irq_handler[TIMER2_INDEX](); // 调用注册的用户中断处理函数
}
}
}

// ... 其他定时器中断处理函数 (TIMER3_IRQHandler, TIMER4_IRQHandler, ...)
  • hal_uart.h: UART驱动头文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef HAL_UART_H
#define HAL_UART_H

#include "gd32f30x.h"

typedef enum {
UART_BAUDRATE_9600 = 9600,
UART_BAUDRATE_19200 = 19200,
UART_BAUDRATE_38400 = 38400,
UART_BAUDRATE_57600 = 57600,
UART_BAUDRATE_115200 = 115200
} uart_baudrate_t;

void hal_uart_init(uint32_t uart_periph, uart_baudrate_t baudrate);
void hal_uart_send_byte(uint32_t uart_periph, uint8_t data);
void hal_uart_send_string(uint32_t uart_periph, const char *str);
uint8_t hal_uart_receive_byte(uint32_t uart_periph);
void hal_uart_enable_rx_irq(uint32_t uart_periph, uint8_t priority, void (*irq_handler)(void));

#endif // HAL_UART_H
  • hal_uart.c: UART驱动源文件
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
#include "hal_uart.h"
#include <stdio.h> // For printf

void hal_uart_init(uint32_t uart_periph, uart_baudrate_t baudrate) {
usart_parameter_struct usart_initpara;

rcu_periph_clock_enable(GET_UART_CLK(uart_periph)); // 使能UART时钟

usart_deinit(uart_periph);

usart_struct_para_init(&usart_initpara);
usart_initpara.baudrate = baudrate;
usart_initpara.word_length = USART_WL_8BIT;
usart_initpara.stop_bits = USART_STB_1BIT;
usart_initpara.parity = USART_PM_NONE;
usart_initpara.flow_control = USART_FLOW_CTRL_NONE;
usart_initpara.mode = USART_MODE_TX | USART_MODE_RX; // 使能发送和接收
usart_init(uart_periph, &usart_initpara);

usart_enable(uart_periph);
}

void hal_uart_send_byte(uint32_t uart_periph, uint8_t data) {
usart_data_transmit(uart_periph, data);
while (RESET == usart_flag_get(uart_periph, USART_FLAG_TBE)); // 等待发送缓冲区空
}

void hal_uart_send_string(uint32_t uart_periph, const char *str) {
while (*str) {
hal_uart_send_byte(uart_periph, *str++);
}
}

uint8_t hal_uart_receive_byte(uint32_t uart_periph) {
while (RESET == usart_flag_get(uart_periph, USART_FLAG_RBNE)); // 等待接收缓冲区非空
return usart_data_receive(uart_periph);
}

void hal_uart_enable_rx_irq(uint32_t uart_periph, uint8_t priority, void (*irq_handler)(void)) {
nvic_irq_enable(GET_UART_IRQN(uart_periph), priority, 0); // 使能UART中断
usart_interrupt_enable(uart_periph, USART_INT_RBNE); // 使能接收缓冲区非空中断
uart_irq_handler[GET_UART_INDEX(uart_periph)] = irq_handler; // 注册中断处理函数
}


// UART 中断处理函数表
void (*uart_irq_handler[UART_MAX_INDEX])(void) = {NULL};

// 通用UART中断处理函数 (需要根据实际使用的UART修改)
void USART0_IRQHandler(void) {
if (usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE) != RESET) {
usart_interrupt_flag_clear(USART0, USART_INT_FLAG_RBNE);
if (uart_irq_handler[UART0_INDEX] != NULL) {
uart_irq_handler[UART0_INDEX](); // 调用注册的用户中断处理函数
}
}
}

// ... 其他UART中断处理函数 (USART1_IRQHandler, USART2_IRQHandler, ...)

// 重定向printf到UART (例如 USART0)
int fputc(int ch, FILE *f) {
hal_uart_send_byte(USART0, (uint8_t)ch);
return ch;
}
  • hal_spi.h, hal_spi.c, hal_i2c.h, hal_i2c.c, hal_adc.h, hal_adc.c, hal_dac.h, hal_dac.c: 类似地,可以创建SPI、I2C、ADC、DAC等HAL驱动文件,提供统一的接口。这里省略具体代码,原理类似GPIO、Timer、UART。

3.2 BSP层 (Board Support Package)

  • bsp.h: BSP头文件
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
#ifndef BSP_H
#define BSP_H

#include "gd32f30x.h"
#include "hal_gpio.h"
#include "hal_timer.h"
#include "hal_uart.h"
// ... 其他HAL头文件

// 系统时钟配置
void bsp_system_clock_config(void);

// 外设初始化
void bsp_peripherals_init(void);

// LED 初始化 (示例,根据实际板载LED修改)
void bsp_led_init(void);
void bsp_led_on(uint8_t led_num);
void bsp_led_off(uint8_t led_num);
void bsp_led_toggle(uint8_t led_num);

// 按键 初始化 (示例,根据实际板载按键修改)
void bsp_key_init(void);
uint8_t bsp_key_read(uint8_t key_num);

// 延时函数 (基于SysTick)
void delay_ms(uint32_t ms);
void delay_us(uint32_t us);

#endif // BSP_H
  • bsp.c: BSP源文件
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
#include "bsp.h"

// 系统时钟配置 (示例,根据实际晶振频率和需求配置)
void bsp_system_clock_config(void) {
rcu_clock_freq_set(RCU_IRC8M, RCU_CK_MUL16); // 8MHz IRC8M * 16 = 128MHz
rcu_osci_on(RCU_IRC8M);
while(ERROR == rcu_osci_stab_wait(RCU_IRC8M));

rcu_system_clock_source_config(RCU_CKSYSRC_IRC8M); // 选择IRC8M作为系统时钟源

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

// 外设初始化
void bsp_peripherals_init(void) {
bsp_led_init();
bsp_key_init();
// ... 其他外设初始化,例如 UART1 for debug
hal_uart_init(USART0, UART_BAUDRATE_115200);
}

// LED 初始化 (示例)
void bsp_led_init(void) {
hal_gpio_init(GPIOC, GPIO_PIN_13, GPIO_MODE_OUTPUT, GPIO_SPEED_LOW, GPIO_PUPD_NONE); // 例如 PC13 连接 LED
hal_gpio_write_pin(GPIOC, GPIO_PIN_13, 1); // 默认熄灭
}

void bsp_led_on(uint8_t led_num) {
if (led_num == 0) hal_gpio_write_pin(GPIOC, GPIO_PIN_13, 0); // LED 亮
}

void bsp_led_off(uint8_t led_num) {
if (led_num == 0) hal_gpio_write_pin(GPIOC, GPIO_PIN_13, 1); // LED 灭
}

void bsp_led_toggle(uint8_t led_num) {
if (led_num == 0) {
if (hal_gpio_read_pin(GPIOC, GPIO_PIN_13)) {
hal_gpio_write_pin(GPIOC, GPIO_PIN_13, 0);
} else {
hal_gpio_write_pin(GPIOC, GPIO_PIN_13, 1);
}
}
}

// 按键 初始化 (示例)
void bsp_key_init(void) {
hal_gpio_init(GPIOA, GPIO_PIN_0, GPIO_MODE_INPUT, GPIO_SPEED_LOW, GPIO_PUPD_PULLUP); // 例如 PA0 连接 按键,上拉输入
}

uint8_t bsp_key_read(uint8_t key_num) {
if (key_num == 0) return !hal_gpio_read_pin(GPIOA, GPIO_PIN_0); // 按键按下返回 1
return 0;
}

// 延时函数 (基于 SysTick)
static __IO uint32_t sys_tick_delay;

void delay_ms(uint32_t ms) {
sys_tick_delay = ms;
while(sys_tick_delay != 0);
}

void delay_us(uint32_t us) {
// 更精确的微秒延时需要更复杂的实现,这里简化为毫秒延时
delay_ms(us / 1000 + 1); // 粗略估计
}

void SysTick_Handler(void) {
if (sys_tick_delay != 0x00) {
sys_tick_delay--;
}
}

// SysTick 初始化 (在main函数中调用)
void bsp_systick_init(void) {
SysTick_Config(SystemCoreClock / 1000); // 1ms 中断一次
}

3.3 驱动层 (Drivers)

  • servo_driver.h: 舵机驱动头文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef SERVO_DRIVER_H
#define SERVO_DRIVER_H

#include "hal_timer.h"

#define SERVO_TIMER_PERIPH TIMER2 // 选择用于舵机控制的定时器
#define SERVO_TIMER_CHANNEL TIMER_CH_0 // 选择定时器通道 (例如 CH0)
#define SERVO_GPIO_PERIPH GPIOA // 选择舵机控制信号输出的GPIO口
#define SERVO_GPIO_PIN GPIO_PIN_0 // 选择舵机控制信号输出的GPIO引脚

// 舵机角度范围 (根据实际舵机调整)
#define SERVO_MIN_ANGLE 0
#define SERVO_MAX_ANGLE 180

// PWM 脉宽范围 (根据实际舵机和定时器配置调整)
#define SERVO_MIN_PULSE 500 // 0.5ms
#define SERVO_MAX_PULSE 2500 // 2.5ms

void servo_driver_init(void);
void servo_set_angle(uint8_t servo_id, uint8_t angle); // 控制单个舵机角度
void servo_set_angles(uint8_t angles[]); // 控制多个舵机角度 (数组形式)

#endif // SERVO_DRIVER_H
  • servo_driver.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
#include "servo_driver.h"

// 将角度转换为 PWM 脉宽
static uint16_t angle_to_pulse(uint8_t angle) {
if (angle > SERVO_MAX_ANGLE) angle = SERVO_MAX_ANGLE;
uint32_t pulse_range = SERVO_MAX_PULSE - SERVO_MIN_PULSE;
uint16_t pulse = SERVO_MIN_PULSE + (uint16_t)((uint32_t)pulse_range * angle / SERVO_MAX_ANGLE);
return pulse;
}

void servo_driver_init(void) {
timer_oc_parameter_struct timer_ocinitpara;

hal_timer_init(SERVO_TIMER_PERIPH, 71, 20000 - 1); // 72MHz / (71+1) = 1MHz, 周期 20ms (50Hz)
// PWM 频率 = 1MHz / 20000 = 50Hz

/* PWM channel configuration */
timer_ocinitpara.outputstate = TIMER_CCX_ENABLE;
timer_ocinitpara.outputnstate = TIMER_CCXN_DISABLE;
timer_ocinitpara.ocpolarity = TIMER_OC_POLARITY_HIGH;
timer_ocinitpara.ocnpolarity = TIMER_OCN_POLARITY_HIGH;
timer_ocinitpara.ocidlestate = TIMER_OC_IDLE_STATE_LOW;
timer_ocinitpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;

timer_channel_output_config(SERVO_TIMER_PERIPH, SERVO_TIMER_CHANNEL, &timer_ocinitpara);

/* PWM output mode configuration */
timer_channel_output_pulse_value_config(SERVO_TIMER_PERIPH, SERVO_TIMER_CHANNEL, angle_to_pulse(0)); // 初始角度 0 度
timer_channel_output_mode_config(SERVO_TIMER_PERIPH, SERVO_TIMER_CHANNEL, TIMER_OC_MODE_PWM0);
timer_channel_output_shadow_config(SERVO_TIMER_PERIPH, SERVO_TIMER_CHANNEL, TIMER_OC_SHADOW_DISABLE);

/* 使能 PWM 输出通道 */
timer_channel_enable(SERVO_TIMER_PERIPH, SERVO_TIMER_CHANNEL);

hal_timer_start(SERVO_TIMER_PERIPH); // 启动定时器
}

void servo_set_angle(uint8_t servo_id, uint8_t angle) {
uint16_t pulse = angle_to_pulse(angle);
timer_channel_output_pulse_value_config(SERVO_TIMER_PERIPH, SERVO_TIMER_CHANNEL, pulse);
}

void servo_set_angles(uint8_t angles[]) {
// 假设 angles 数组长度为 8,对应 8 路舵机
for (int i = 0; i < 8; i++) {
servo_set_angle(i, angles[i]); // 这里假设 servo_id 与数组索引对应
}
}
  • lcd_driver.h, lcd_driver.c, camera_driver.h, camera_driver.c, voice_ic_driver.h, voice_ic_driver.c, bluetooth_driver.h, bluetooth_driver.c, rf24g_driver.h, rf24g_driver.c, lora_driver.h, lora_driver.c: 类似地,可以创建LCD、摄像头、语音IC、蓝牙、2.4G、Lora等驱动文件。这里省略具体代码,需要根据具体的LCD型号、摄像头模块、语音IC型号、无线模块型号选择合适的驱动方案和代码实现。例如:

    • LCD驱动: 可能需要SPI或并行接口,根据LCD驱动芯片 (例如 ST7735, ST7789) 编写初始化、显示字符、显示图像等函数。
    • 摄像头驱动: 可能需要配置GPIO、DMA等,根据摄像头模块的接口协议 (例如 DVP, SPI) 编写初始化、图像采集、数据传输等函数。
    • 语音IC驱动: 可能需要UART或I2C接口,根据语音IC的通信协议 (例如串口命令、I2C寄存器) 编写初始化、语音识别、语音合成等函数。
    • 无线模块驱动: 通常基于UART或SPI接口,根据无线模块的通信协议 (例如 AT 指令, 专有协议) 编写初始化、数据发送、数据接收等函数。

3.4 通信层 (Communication Layer)

  • communication_manager.h: 通信管理头文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef COMMUNICATION_MANAGER_H
#define COMMUNICATION_MANAGER_H

#include "bluetooth_driver.h"
#include "rf24g_driver.h"
#include "lora_driver.h"

// 遥控指令结构体 (根据实际遥控协议定义)
typedef struct {
uint8_t command_type; // 指令类型 (例如 运动控制, 舵机控制, 功能指令等)
uint8_t data[16]; // 指令数据
} remote_command_t;

void communication_manager_init(void);
void communication_process(void); // 通信数据处理主循环
void parse_remote_command(const uint8_t *data, uint16_t length); // 解析遥控指令

#endif // COMMUNICATION_MANAGER_H
  • communication_manager.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
#include "communication_manager.h"
#include "servo_driver.h" // 假设舵机控制放在 servo_driver.c 中
#include "motion_control.h" // 假设运动控制放在 motion_control.c 中
#include <stdio.h> // For printf

// 接收缓冲区 (示例,根据实际通信协议和数据量调整)
#define RX_BUFFER_SIZE 64
uint8_t rx_buffer[RX_BUFFER_SIZE];
uint16_t rx_buffer_index = 0;

void communication_manager_init(void) {
bluetooth_driver_init();
rf24g_driver_init();
lora_driver_init();

// 初始化接收缓冲区
rx_buffer_index = 0;
memset(rx_buffer, 0, sizeof(rx_buffer));

// 启用蓝牙接收中断 (示例,假设蓝牙驱动支持中断接收)
bluetooth_enable_rx_irq(communication_bluetooth_rx_handler);
// ... 启用 2.4G, Lora 接收中断 (如果需要)
}

void communication_process(void) {
// 主循环中可以轮询检查接收缓冲区,或者在中断处理函数中直接处理数据

if (rx_buffer_index > 0) { // 接收到数据
parse_remote_command(rx_buffer, rx_buffer_index);
rx_buffer_index = 0; // 清空接收缓冲区
memset(rx_buffer, 0, sizeof(rx_buffer));
}

// ... 其他通信处理逻辑,例如发送状态信息等
}

void parse_remote_command(const uint8_t *data, uint16_t length) {
if (length < 1) return; // 数据长度不足

remote_command_t command;
memcpy(&command, data, sizeof(remote_command_t)); // 将接收到的数据解析为指令结构体

switch (command.command_type) {
case 0x01: // 运动控制指令
printf("运动控制指令\r\n");
motion_control_process_command(command.data); // 调用运动控制模块处理指令
break;
case 0x02: // 舵机控制指令
printf("舵机控制指令\r\n");
servo_set_angles(command.data); // 调用舵机驱动设置角度
break;
case 0x03: // 功能指令 (例如 LED 控制, 语音交互触发等)
printf("功能指令\r\n");
// ... 处理功能指令
break;
default:
printf("未知指令类型: %x\r\n", command.command_type);
break;
}
}

// 蓝牙接收中断处理函数 (示例)
void communication_bluetooth_rx_handler(void) {
uint8_t data = bluetooth_receive_byte();
rx_buffer[rx_buffer_index++] = data;
if (rx_buffer_index >= RX_BUFFER_SIZE) {
rx_buffer_index = RX_BUFFER_SIZE - 1; // 防止缓冲区溢出
}
}

// ... 2.4G, Lora 接收中断处理函数 (如果需要)

3.5 应用层 (Application Layer)

  • 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
31
32
33
#include "bsp.h"
#include "servo_driver.h"
#include "lcd_driver.h"
#include "camera_driver.h"
#include "voice_ic_driver.h"
#include "communication_manager.h"
#include "motion_control.h" // 假设运动控制模块放在 motion_control.h/motion_control.c 中
#include <stdio.h>

int main(void) {
bsp_system_clock_config(); // 配置系统时钟
bsp_systick_init(); // 初始化 SysTick
bsp_peripherals_init(); // 初始化外设 (LED, 按键, UART等)

servo_driver_init(); // 初始化舵机驱动
lcd_driver_init(); // 初始化 LCD 驱动
camera_driver_init(); // 初始化摄像头驱动
voice_ic_driver_init(); // 初始化语音交互模块驱动
communication_manager_init(); // 初始化通信管理器
motion_control_init(); // 初始化运动控制模块

printf("瓦力机器人系统启动!\r\n");
lcd_display_string(0, 0, "WALL-E Robot"); // LCD 显示欢迎信息

while (1) {
communication_process(); // 处理通信数据
motion_control_task(); // 运动控制任务 (例如状态机切换, 速度控制等)
// ... 其他应用层任务 (例如 语音交互处理, 图像传输处理, LCD 显示更新等)

bsp_led_toggle(0); // LED 闪烁,指示程序运行
delay_ms(500);
}
}
  • motion_control.h, motion_control.c: 运动控制模块,负责机器人的运动逻辑,例如前进、后退、转弯、停止等。可以使用状态机来管理机器人的运动状态,根据遥控指令控制舵机或电机驱动器。
  • lcd_display_driver.h, lcd_display_driver.c: LCD显示驱动模块,负责在LCD屏幕上显示文字、图像、状态信息等。
  • voice_interaction.h, voice_interaction.c: 语音交互模块,负责处理语音输入和语音输出,实现语音控制和语音反馈功能。
  • image_transmission.h, image_transmission.c: 图像传输模块,负责采集摄像头图像,并通过无线通信模块 (例如 2.4G) 实时传输图像数据到上位机。
  • state_management.h, state_management.c: 状态管理模块,负责管理机器人的整体状态,例如工作模式、电量状态、错误状态等。
  • error_handler.h, error_handler.c: 错误处理模块,负责检测系统错误,记录错误信息,并进行错误处理和恢复。

4. 开发流程

  1. 需求分析: 明确瓦力机器人的功能需求,例如遥控方式、运动模式、交互方式、图像传输需求等。
  2. 硬件选型: 根据需求选择合适的GD32微控制器、蓝牙/2.4G/Lora模块、舵机、LCD屏、摄像头、语音IC等硬件器件。
  3. 系统架构设计: 根据硬件和需求,设计软件系统架构,包括分层架构、模块划分、接口定义等。
  4. HAL层开发: 编写底层硬件驱动,例如GPIO、Timer、UART、SPI、I2C、ADC、DAC等HAL驱动。
  5. BSP层开发: 配置系统时钟、外设初始化、中断管理等BSP代码,搭建硬件平台基础。
  6. 驱动层开发: 编写外围器件驱动,例如舵机驱动、LCD驱动、摄像头驱动、语音交互模块驱动、无线通信模块驱动等。
  7. 通信层开发: 实现蓝牙、2.4G、Lora通信协议,解析遥控指令。
  8. 应用层开发: 编写核心应用逻辑,例如运动控制、舵机控制、LCD显示、语音交互、图像传输等。
  9. 系统集成和测试: 将各个模块集成起来,进行系统测试,包括功能测试、性能测试、稳定性测试等。
  10. 维护升级: 根据用户反馈和需求变化,进行软件维护和升级,例如修复bug、增加新功能、优化性能等。

5. 测试验证和维护升级

  • 测试验证:
    • 单元测试: 对每个模块进行单元测试,验证模块功能的正确性。
    • 集成测试: 将模块集成起来进行集成测试,验证模块之间的协同工作是否正常。
    • 系统测试: 对整个系统进行系统测试,验证系统功能是否满足需求,性能是否达标,稳定性是否可靠。
    • 实际场景测试: 将瓦力机器人放到实际场景中进行测试,例如草地、室内等,验证机器人在真实环境下的工作性能。
  • 维护升级:
    • Bug修复: 及时修复测试和使用过程中发现的bug,提高系统稳定性。
    • 功能扩展: 根据用户需求和技术发展,增加新的功能,例如自主导航、更智能的语音交互等。
    • 性能优化: 优化代码和算法,提高系统性能,例如提高运动控制的精度和响应速度,降低功耗等。
    • 固件升级: 提供固件升级机制,方便用户进行软件升级,例如通过串口、OTA (Over-The-Air) 等方式进行固件升级。

总结

以上代码和架构设计方案提供了一个基于GD32的瓦力机器人的嵌入式系统开发框架。实际项目中,代码量会远超3000行,需要根据具体硬件和功能需求进行详细设计和实现。 通过分层架构和模块化设计,结合实践验证的技术和方法,可以构建一个可靠、高效、可扩展的嵌入式系统平台,最终实现一个功能完善、性能优良的瓦力机器人。 希望这个详细的解答能够帮助您理解嵌入式系统开发的完整流程和关键技术。

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