4. LED驱动基础实现:单LED点亮与闪烁、使用SysTick定时器实现非阻塞延时、多LED流水灯实现
4.1 单LED点亮:GPIO输出模式配置
在XMC系列MCU中,控制LED的第一步是将对应的GPIO引脚配置为推挽输出(Push-Pull Output)模式。以XMC1300系列为例,LED通常连接在P1.0引脚(高电平点亮)。
| 配置步骤 | 寄存器/函数 | 说明 |
|---|---|---|
| 1. 使能端口时钟 | SCU_CLK->CLKCR |
设置对应端口位时钟使能 |
| 2. 配置引脚模式 | P1_IOCR0 (P1.0) |
写入 0x80 为推挽输出 |
| 3. 设置输出电平 | P1_OUT |
写1点亮,写0熄灭 |
// 单LED点亮示例:XMC1300 P1.0
#include "xmc_gpio.h"
#define LED_PIN P1_0
void LED_Init(void) {
// 配置P1.0为推挽输出,初始低电平
XMC_GPIO_SetMode(LED_PIN, XMC_GPIO_MODE_OUTPUT_PUSH_PULL);
XMC_GPIO_SetOutputLow(LED_PIN);
}
void LED_On(void) {
XMC_GPIO_SetOutputHigh(LED_PIN);
}
void LED_Off(void) {
XMC_GPIO_SetOutputLow(LED_PIN);
}
int main(void) {
LED_Init();
LED_On(); // 点亮LED
while(1);
}
硬件确认: 实际开发中务必核对原理图,确认LED是高电平点亮还是低电平点亮。若为低电平点亮,则需配置为
XMC_GPIO_MODE_OUTPUT_PUSH_PULL_ALT2或使用SetOutputLow/SetOutputHigh反向操作。
4.2 使用SysTick定时器实现非阻塞延时
传统的for循环阻塞延时浪费CPU资源,且无法处理其他任务。XMC系列MCU内置SysTick定时器(24位递减计数器),非常适合生成精确的非阻塞延时。
4.2.1 SysTick工作原理
4.2.2 非阻塞延时实现代码
// SysTick非阻塞延时驱动
#include "xmc_scu.h"
static volatile uint32_t sysTickCounter = 0;
// SysTick中断服务函数(1ms周期)
void SysTick_Handler(void) {
sysTickCounter++;
}
// 初始化SysTick,产生1ms中断
void SysTick_Init(void) {
// 系统时钟频率 32MHz,重装载值 32000-1 = 1ms
SysTick_Config(SystemCoreClock / 1000);
}
// 获取当前tick值
uint32_t GetTick(void) {
return sysTickCounter;
}
// 非阻塞延时函数
void Delay_NonBlocking(uint32_t ms) {
uint32_t start = GetTick();
while ((GetTick() - start) < ms) {
// 此处可插入其他任务,如按键扫描、通信处理等
// 实现真正的非阻塞
}
}
// 使用示例:LED闪烁(非阻塞)
void LED_Blink_NonBlocking(void) {
static uint32_t lastTick = 0;
uint32_t now = GetTick();
if ((now - lastTick) >= 500) { // 500ms切换一次
lastTick = now;
XMC_GPIO_ToggleOutput(LED_PIN);
}
}
关键优势: 非阻塞延时函数
Delay_NonBlocking在等待期间CPU可以执行其他任务,而LED_Blink_NonBlocking函数则完全避免了while等待,适合在超级循环(super loop)中周期调用。
4.3 多LED流水灯实现
流水灯是嵌入式入门的经典实验,通过依次点亮多个LED形成“流水”效果。在XMC系列上,我们可以利用位带操作(Bit-Banding)或直接寄存器赋值来高效控制多个引脚。
4.3.1 硬件连接与引脚定义
| LED编号 | MCU引脚 | 端口寄存器位 |
|---|---|---|
| LED1 | P1.0 | P1_OUT bit 0 |
| LED2 | P1.1 | P1_OUT bit 1 |
| LED3 | P1.2 | P1_OUT bit 2 |
| LED4 | P1.3 | P1_OUT bit 3 |
4.3.2 流水灯状态机设计
4.3.3 流水灯完整代码
// 4个LED流水灯实现(非阻塞)
#include "xmc_gpio.h"
#define LED_PORT P1
#define LED1_PIN P1_0
#define LED2_PIN P1_1
#define LED3_PIN P1_2
#define LED4_PIN P1_3
// 流水灯状态表:依次点亮LED1~LED4
const uint8_t flowPattern[] = {
0x01, // 0b0001 -> LED1亮
0x02, // 0b0010 -> LED2亮
0x04, // 0b0100 -> LED3亮
0x08 // 0b1000 -> LED4亮
};
void LED_Flow_Init(void) {
// 配置4个引脚为推挽输出
XMC_GPIO_SetMode(LED1_PIN, XMC_GPIO_MODE_OUTPUT_PUSH_PULL);
XMC_GPIO_SetMode(LED2_PIN, XMC_GPIO_MODE_OUTPUT_PUSH_PULL);
XMC_GPIO_SetMode(LED3_PIN, XMC_GPIO_MODE_OUTPUT_PUSH_PULL);
XMC_GPIO_SetMode(LED4_PIN, XMC_GPIO_MODE_OUTPUT_PUSH_PULL);
// 初始全部熄灭
LED_PORT->OUT = 0x00;
}
void LED_Flow_Run(void) {
static uint8_t index = 0;
static uint32_t lastTick = 0;
uint32_t now = GetTick();
// 每200ms切换一次流水灯状态
if ((now - lastTick) >= 200) {
lastTick = now;
// 直接写入端口输出寄存器,实现并行更新
LED_PORT->OUT = flowPattern[index];
// 索引循环
index++;
if (index >= 4) {
index = 0;
}
}
}
int main(void) {
SysTick_Init(); // 初始化SysTick
LED_Flow_Init(); // 初始化LED
while(1) {
LED_Flow_Run(); // 非阻塞流水灯
// 此处可添加其他任务,如按键扫描
}
}
注意: 直接操作
LED_PORT->OUT寄存器时,务必确保只修改低4位(bit0~bit3),避免影响同一端口其他引脚的状态。若需保留其他位,应使用读-改-写操作:LED_PORT->OUT = (LED_PORT->OUT & 0xF0) | flowPattern[index];
4.4 三种实现方式对比
| 实现方式 | CPU占用 | 实时性 | 可扩展性 | 推荐场景 |
|---|---|---|---|---|
| 阻塞延时(for循环) | 100%占用 | 差 | 低 | 仅用于极简测试 |
| SysTick非阻塞延时 | 仅中断占用 | 良好 | 高 | 多任务、低功耗系统 |
| 硬件定时器+PWM | 几乎为0 | 优秀 | 中 | 固定频率闪烁/呼吸灯 |