1. 开发环境与基础准备
1.1 硬件设备选型与连接
DR1系列评估板作为工业级嵌入式开发平台,其PS端(Processing System)采用双核Cortex-A35架构,主频可达1GHz。开发板配套的AL-LINK-FT-V1.0下载器采用FTDI芯片方案,支持JTAG调试和程序烧录。实际连接时需要注意:
- JTAG接口的1脚(TCK)需与下载器对应引脚严格对齐
- USB转串口模块需使用评估板自带的Type-C接口,避免第三方转换器驱动不兼容
- 开发板供电建议采用官方配套的12V/2A电源适配器,实测电流峰值可达1.8A
关键提示:首次连接时Windows设备管理器可能出现"USB Serial Converter"未驱动情况,需手动安装FTDI提供的CDM驱动程序。
1.2 软件工具链配置
开发环境采用TD_5.9.1_DR1_2025.1_NL(Toolchain Driver)和FD_2025.1_SP1(Firmware Developer)组合方案:
- TD工具包含交叉编译工具链(arm-none-eabi-gcc 10.3.1)
- FD集成开发环境提供工程模板和调试插件
- 环境变量需设置AL_TOOLCHAIN_PATH指向工具链安装目录
典型安装目录结构示例:
code复制C:\AL_Tools
├── TD_5.9.1
│ ├── bin
│ ├── lib
│ └── arm-none-eabi
└── FD_2025.1
├── projects
└── plugins
1.3 工程目录规范
案例工程遵循严格的目录管理规范:
code复制Baremetal-demos/
├── led_flash_a35
│ ├── project
│ │ └── led_flash
│ │ ├── src
│ │ └── Makefile
│ └── bin
│ └── led_flash.bin
RTOS-demos/
├── key_led_a35
│ ├── project
│ │ └── key_led
│ │ ├── FreeRTOS
│ │ └── app_src
│ └── bin
│ └── key_led.bin
2. LED闪烁案例深度解析
2.1 硬件原理与寄存器配置
评估板LED2连接在PS端GPIO Bank1的第14引脚,硬件原理图显示采用共阳极设计,需输出低电平点亮。GPIO控制器关键寄存器:
- GPIO_DIRECTION_CTRL:设置输入/输出模式(0x01为输出)
- GPIO_DATA_OUT:输出数据寄存器
- GPIO_DATA_IN:输入数据寄存器
裸机模式下配置流程:
c复制// 寄存器地址定义
#define GPIO_BANK1_BASE 0xE0304000
#define GPIO_DIR_OFFSET 0x04
#define GPIO_DATA_OFFSET 0x00
void GPIO_Init(void) {
volatile uint32_t *gpio_dir = (uint32_t*)(GPIO_BANK1_BASE + GPIO_DIR_OFFSET);
volatile uint32_t *gpio_data = (uint32_t*)(GPIO_BANK1_BASE + GPIO_DATA_OFFSET);
// 设置GPIO14为输出模式
*gpio_dir |= (1 << 14);
// 初始状态设为高电平(LED灭)
*gpio_data |= (1 << 14);
}
2.2 裸机定时实现方案
精确500ms延时采用APB定时器实现,时钟源为100MHz:
c复制#define TIMER_LOAD_VALUE (500 * 100 * 1000) // 500ms @ 100MHz
void delay_ms(uint32_t ms) {
uint32_t load = ms * 100 * 1000;
TIMER->LOAD = load;
TIMER->CTRL = TIMER_CTRL_ENABLE;
while(!(TIMER->INTSTATUS & TIMER_INT_MATCH));
TIMER->INTSTATUS = TIMER_INT_MATCH; // 清除中断标志
}
2.3 FreeRTOS任务设计
在RTOS版本中,LED控制被封装为独立任务:
c复制void vLEDTask(void *pvParameters) {
const TickType_t xDelay = pdMS_TO_TICKS(500);
for(;;) {
vTaskDelay(xDelay);
GPIO_Toggle(BANK1, PIN14);
// 调试信息输出
printf("[RTOS] LED state: %d\r\n", GPIO_Read(BANK1, PIN14));
}
}
任务调度配置要点:
- 设置configTICK_RATE_HZ=1000(1ms时间片)
- 堆栈大小至少128字(实测低于96字会导致栈溢出)
- 优先级建议设为tskIDLE_PRIORITY + 1
3. 按键控制LED案例实战
3.1 按键消抖算法优化
原始代码采用简单延时消抖,改进方案使用状态机:
c复制typedef enum {
KEY_STATE_RELEASED,
KEY_STATE_DEBOUNCE,
KEY_STATE_PRESSED
} KeyState;
KeyState keyDetect(void) {
static KeyState state = KEY_STATE_RELEASED;
static uint32_t tick = 0;
switch(state) {
case KEY_STATE_RELEASED:
if(GPIO_Read(KEY_PIN) == 0) {
state = KEY_STATE_DEBOUNCE;
tick = xTaskGetTickCount();
}
break;
case KEY_STATE_DEBOUNCE:
if((xTaskGetTickCount() - tick) > pdMS_TO_TICKS(20)) {
state = GPIO_Read(KEY_PIN) ? KEY_STATE_RELEASED : KEY_STATE_PRESSED;
}
break;
case KEY_STATE_PRESSED:
if(GPIO_Read(KEY_PIN)) {
state = KEY_STATE_RELEASED;
}
break;
}
return state;
}
3.2 FreeRTOS队列通信
按键事件通过队列传递给LED任务:
c复制QueueHandle_t xKeyQueue = xQueueCreate(5, sizeof(uint8_t));
// 按键检测任务
void vKeyTask(void *pvParameters) {
uint8_t key_event;
for(;;) {
if(keyDetect() == KEY_STATE_PRESSED) {
key_event = 1;
xQueueSend(xKeyQueue, &key_event, portMAX_DELAY);
}
}
}
// LED控制任务
void vLEDTask(void *pvParameters) {
uint8_t received_event;
for(;;) {
if(xQueueReceive(xKeyQueue, &received_event, portMAX_DELAY) == pdPASS) {
GPIO_Toggle(LED_PIN);
}
}
}
3.3 中断驱动方案
高性能场景可采用GPIO中断模式:
c复制void GPIO_IRQHandler(void) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
uint8_t key_event = 1;
if(GPIO_GetITStatus(KEY_PIN)) {
xQueueSendFromISR(xKeyQueue, &key_event, &xHigherPriorityTaskWoken);
GPIO_ClearITPendingBit(KEY_PIN);
}
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
配置要点:
- 设置GPIO为双边沿触发
- 中断优先级配置为中等优先级(避免影响系统关键中断)
- ISR中必须使用FromISR版本的API
4. 串口通信案例进阶
4.1 串口参数优化配置
UART1采用16550兼容控制器,关键参数设置:
c复制typedef struct {
uint32_t baudrate; // 波特率(1200-115200)
uint8_t data_bits; // 数据位(5-8)
uint8_t stop_bits; // 停止位(1-2)
uint8_t parity; // 校验位(0-none, 1-odd, 2-even)
uint8_t flow_ctrl; // 流控(0-none, 1-RTS/CTS)
} UART_Config;
void UART_Init(UART_Type *uart, UART_Config *config) {
// 计算分频值(输入时钟50MHz)
uint16_t divisor = 50000000 / (16 * config->baudrate);
uart->LCR |= UART_LCR_DLAB; // 使能分频寄存器访问
uart->DLL = divisor & 0xFF;
uart->DLH = (divisor >> 8) & 0xFF;
uart->LCR &= ~UART_LCR_DLAB; // 关闭分频寄存器访问
// 设置线路控制参数
uart->LCR = (config->data_bits - 5) |
(config->stop_bits << 2) |
(config->parity << 3);
// 使能FIFO(16字节深度)
uart->FCR = UART_FCR_FIFO_EN | UART_FCR_RX_FIFO_RST | UART_FCR_TX_FIFO_RST;
}
4.2 环形缓冲区实现
高效数据缓存采用环形缓冲区:
c复制typedef struct {
uint8_t *buffer;
uint16_t head;
uint16_t tail;
uint16_t size;
uint16_t count;
} RingBuffer;
void RingBuf_Init(RingBuffer *rbuf, uint8_t *buf, uint16_t size) {
rbuf->buffer = buf;
rbuf->size = size;
rbuf->head = rbuf->tail = rbuf->count = 0;
}
bool RingBuf_Put(RingBuffer *rbuf, uint8_t data) {
if(rbuf->count >= rbuf->size) return false;
rbuf->buffer[rbuf->head++] = data;
if(rbuf->head >= rbuf->size) rbuf->head = 0;
rbuf->count++;
return true;
}
bool RingBuf_Get(RingBuffer *rbuf, uint8_t *data) {
if(rbuf->count == 0) return false;
*data = rbuf->buffer[rbuf->tail++];
if(rbuf->tail >= rbuf->size) rbuf->tail = 0;
rbuf->count--;
return true;
}
4.3 FreeRTOS流缓冲区应用
大数据量传输建议使用Stream Buffer:
c复制StreamBufferHandle_t xUartStream = xStreamBufferCreate(256, 1);
// 接收任务
void vUartRecvTask(void *pvParameters) {
uint8_t rx_data;
for(;;) {
if(xStreamBufferReceive(xUartStream, &rx_data, 1, portMAX_DELAY)) {
// 处理接收数据
}
}
}
// 中断服务程序
void UART_IRQHandler(void) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
uint8_t data = UART->DR;
xStreamBufferSendFromISR(xUartStream, &data, 1, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
5. 调试技巧与性能优化
5.1 内存使用分析
FreeRTOS内存检测方法:
c复制// 在任务中插入检测点
void vTaskMemoryCheck(void *pvParameters) {
for(;;) {
printf("Free heap: %u\r\n", xPortGetFreeHeapSize());
printf("Min free heap: %u\r\n", xPortGetMinimumEverFreeHeapSize());
vTaskDelay(pdMS_TO_TICKS(5000));
}
}
5.2 任务运行状态监控
使用FreeRTOS运行时间统计:
- 配置configGENERATE_RUN_TIME_STATS=1
- 实现portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()
- 调用vTaskGetRunTimeStats()输出报表
5.3 功耗优化策略
低功耗设计要点:
- 空闲任务中使用WFI指令
- 动态调整CPU频率(需硬件支持)
- 外设时钟门控管理
- 任务调度策略优化(使用tickless模式)
典型配置示例:
c复制void vApplicationIdleHook(void) {
__asm volatile("wfi");
// 进入低功耗模式前处理
Peripheral_ClockGate();
CPU_FrequencyScale(CLK_DIV_4);
}