【二】LED广告灯
1. 项目概述
本项目是一个基于STM32F103C8T6微控制器的LED广告灯示例程序,实现了10种不同的LED动态显示效果。项目使用STM32标准外设库(V3.5.0)开发,展示了丰富多样的LED控制模式。
2. 硬件平台
- MCU型号:STM32F103C8T6
- 核心架构:ARM Cortex-M3
- 开发板:成都烁云科技开发板
- LED连接:GPIOB端口(PB0~PB7)
- LED数量:8个
- 标准库版本:STM32F10x Standard Peripheral Library V3.5.0
3. 文件结构
2_LSD广告灯/
├── User/ # 用户代码目录
│ ├── main.c # 主程序文件 - 演示各种广告灯效果
│ ├── stm32f10x_conf.h # 标准库配置文件
│ ├── stm32f10x_it.c # 中断服务函数
│ ├── stm32f10x_it.h # 中断头文件
│ ├── system_stm32f10x.c # 系统初始化文件
│ └── TF_App/ # 应用层驱动
│ ├── TF_LED.c # LED驱动实现(含10种特效)
│ ├── TF_LED.h # LED驱动头文件
│ ├── TF_Delay.c # 延时函数实现
│ └── TF_Delay.h # 延时函数头文件
├── Libraries/ # STM32标准外设库
│ ├── CMSIS/ # ARM CMSIS核心支持
│ └── STM32F10x_StdPeriph_Driver/ # 标准外设驱动
├── MDK/ # Keil工程文件
└── Readme/ # 说明文档
4. 核心模块功能
4.1 主程序模块 (main.c)
主程序循环演示10种不同的LED广告灯效果,每种效果运行后有500ms或1000ms的间隔。
演示效果列表:
| 序号 | 效果名称 | 函数调用 | 延时(ms) | 循环次数 |
|---|---|---|---|---|
| 1 | 流水灯(点亮后熄灭) | LED_Effect_Flow(100, 3) | 100 | 3 |
| 2 | 流水灯(依次点亮不熄灭) | LED_Effect_Flow_NoOff(100, 2) | 100 | 2 |
| 3 | 跑马灯(单灯左右移动) | LED_Effect_RunningLight(80, 2) | 80 | 2 |
| 4 | 全部闪烁 | LED_Effect_Blink(200, 5) | 200 | 5 |
| 5 | 奇偶交替闪烁 | LED_Effect_Alternate(150, 5) | 150 | 5 |
| 6 | 中间扩散 | LED_Effect_MiddleSpread(100, 3) | 100 | 3 |
| 7 | 波浪效果 | LED_Effect_Wave(100, 3) | 100 | 3 |
| 8 | 流星效果(拖尾) | LED_Effect_Meteor(80, 2) | 80 | 2 |
| 9 | 二进制计数(0-255) | LED_Effect_BinaryCount(50, 1) | 50 | 1 |
| 10 | 随机闪烁 | LED_Effect_Twinkle(100, 20) | 100 | 20 |
单一效果运行模式:
主程序中提供了注释代码,可以选择单一效果持续运行:
int main(void)
{
LED_Init();
LED_Effect_BinaryCount(50, 0); // 0表示无限循环
}
4.2 LED驱动模块 (TF_LED.c/h)
提供LED的初始化、基础控制和10种广告灯效果功能。
4.2.1 硬件引脚定义
| LED编号 | GPIO引脚 | GPIO端口 | 时钟 |
|---|---|---|---|
| LED1 | GPIO_Pin_0 | GPIOB | RCC_APB2Periph_GPIOB |
| LED2 | GPIO_Pin_1 | GPIOB | RCC_APB2Periph_GPIOB |
| LED3 | GPIO_Pin_2 | GPIOB | RCC_APB2Periph_GPIOB |
| LED4 | GPIO_Pin_3 | GPIOB | RCC_APB2Periph_GPIOB |
| LED5 | GPIO_Pin_4 | GPIOB | RCC_APB2Periph_GPIOB |
| LED6 | GPIO_Pin_5 | GPIOB | RCC_APB2Periph_GPIOB |
| LED7 | GPIO_Pin_6 | GPIOB | RCC_APB2Periph_GPIOB |
| LED8 | GPIO_Pin_7 | GPIOB | RCC_APB2Periph_GPIOB |
注意: 头文件中LED1~LED8的时钟宏定义存在错误(写成了RCC_APB2Periph_GPIOC),但在LED_Init()函数中使用了正确的LED_CLK宏定义,因此不影响实际运行。
4.2.2 基础控制API函数
void LED_Init(void)
功能: 初始化LED所用的GPIO端口
实现细节:
- 使能GPIOB端口时钟
- 使能AFIO时钟并禁用JTAG功能(释放PB3、PB4引脚)
- 配置PB0~PB7为推挽输出模式,速度10MHz
- 初始化后所有LED熄灭
JTAG重映射说明:
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
此配置禁用JTAG-DP功能,保留SW-DP调试功能,使PB3、PB4可作为普通GPIO使用。
void LED_On(unsigned char Led_PIN)
功能: 点亮指定的LED
参数:
Led_PIN: LED编号(1-8),传入0或其他值则点亮所有LED
实现: 使用switch-case语句,通过GPIO_SetBits()函数将对应引脚拉高
示例:
LED_On(1); // 点亮LED1
LED_On(0); // 点亮所有LED
void LED_Off(unsigned char Led_PIN)
功能: 熄灭指定的LED
参数:
Led_PIN: LED编号(1-8),传入0或其他值则熄灭所有LED
实现: 使用switch-case语句,通过GPIO_ResetBits()函数将对应引脚拉低
示例:
LED_Off(1); // 熄灭LED1
LED_Off(0); // 熄灭所有LED
void LED_Toggle(unsigned char Led_PIN)
功能: 翻转指定LED的状态(亮变灭,灭变亮)
参数:
Led_PIN: LED编号(1-8)
实现原理:
- 通过
GPIO_ReadOutputDataBit()读取当前LED状态 - 如果为高电平则拉低,如果为低电平则拉高
- 对每个LED使用独立的case分支处理
示例:
LED_Toggle(1); // LED1状态翻转
void LED_AllOff(void)
功能: 关闭所有LED
实现:
GPIO_ResetBits(LED_PORT, LED_PIN);
void LED_AllOn(void)
功能: 点亮所有LED
实现:
GPIO_SetBits(LED_PORT, LED_PIN);
4.2.3 广告灯效果函数详解
1. LED_Effect_Flow() - 流水灯效果
函数原型:
void LED_Effect_Flow(unsigned int delay_ms, unsigned int loops)
功能描述: 8个LED依次点亮后立即熄灭,形成流水效果
参数:
delay_ms: 每个LED之间的延时时间(毫秒)loops: 循环次数,0表示无限循环
实现逻辑:
for i = 1 to 8:
点亮LED[i]
延时delay_ms
熄灭LED[i]
效果演示: ●○○○○○○○ → ○●○○○○○○ → ... → ○○○○○○○●
2. LED_Effect_Flow_NoOff() - 流水灯效果(不熄灭)
函数原型:
void LED_Effect_Flow_NoOff(unsigned int delay_ms, unsigned int loops)
功能描述: 8个LED依次点亮,点亮后保持,全部点亮后一起熄灭
参数:
delay_ms: 每个LED之间的延时时间(毫秒)loops: 循环次数,0表示无限循环
实现逻辑:
全部熄灭
for i = 1 to 8:
点亮LED[i]
延时delay_ms
全部熄灭
效果演示: ●○○○○○○○ → ●●○○○○○○ → ... → ●●●●●●●● → ○○○○○○○○
3. LED_Effect_RunningLight() - 跑马灯效果
函数原型:
void LED_Effect_RunningLight(unsigned int delay_ms, unsigned int loops)
功能描述: 单个LED从左到右、再从右到左来回移动
参数:
delay_ms: 移动延时时间(毫秒)loops: 循环次数,0表示无限循环
实现逻辑:
// 从左到右
for i = 1 to 8:
全部熄灭
点亮LED[i]
延时delay_ms
// 从右到左
for i = 7 down to 1:
全部熄灭
点亮LED[i]
延时delay_ms
效果演示: ●○○○○○○○ → ○●○○○○○○ → ... → ○○○○○○○● → ○○○○○○●○ → ...
4. LED_Effect_Blink() - 全部闪烁效果
函数原型:
void LED_Effect_Blink(unsigned int delay_ms, unsigned int loops)
功能描述: 所有LED同时闪烁
参数:
delay_ms: 闪烁延时时间(毫秒)loops: 循环次数,0表示无限循环
实现逻辑:
全部点亮
延时delay_ms
全部熄灭
延时delay_ms
效果演示: ●●●●●●●● ↔ ○○○○○○○○
5. LED_Effect_Alternate() - 奇偶交替闪烁
函数原型:
void LED_Effect_Alternate(unsigned int delay_ms, unsigned int loops)
功能描述: 奇数位LED和偶数位LED交替闪烁
参数:
delay_ms: 闪烁延时时间(毫秒)loops: 循环次数,0表示无限循环
实现逻辑:
// 奇数LED点亮
点亮LED1, LED3, LED5, LED7
延时delay_ms
// 偶数LED点亮
点亮LED2, LED4, LED6, LED8
延时delay_ms
效果演示: ●○●○●○●○ ↔ ○●○●○●○●
6. LED_Effect_MiddleSpread() - 中间扩散效果
函数原型:
void LED_Effect_MiddleSpread(unsigned int delay_ms, unsigned int loops)
功能描述: LED从中间向两边逐步扩散点亮
参数:
delay_ms: 延时时间(毫秒)loops: 循环次数,0表示无限循环
实现逻辑:
全部熄灭
点亮LED4, LED5 // ○○○●●○○○
点亮LED3, LED6 // ○○●●●●○○
点亮LED2, LED7 // ○●●●●●●○
点亮LED1, LED8 // ●●●●●●●●
全部熄灭
效果演示: ○○○●●○○○ → ○○●●●●○○ → ○●●●●●●○ → ●●●●●●●●
7. LED_Effect_Wave() - 波浪效果
函数原型:
void LED_Effect_Wave(unsigned int delay_ms, unsigned int loops)
功能描述: 3个连续的LED作为一组向右移动
参数:
delay_ms: 延时时间(毫秒)loops: 循环次数,0表示无限循环
实现逻辑:
点亮LED1,2,3 // ●●●○○○○○
点亮LED2,3,4 // ○●●●○○○○
点亮LED3,4,5 // ○○●●●○○○
点亮LED4,5,6 // ○○○●●●○○
点亮LED5,6,7 // ○○○○●●●○
点亮LED6,7,8 // ○○○○○●●●
效果演示: 形成波浪向右推进的视觉效果
8. LED_Effect_Meteor() - 流星效果
函数原型:
void LED_Effect_Meteor(unsigned int delay_ms, unsigned int loops)
功能描述: LED拖尾点亮,形成流星划过的效果
参数:
delay_ms: 延时时间(毫秒)loops: 循环次数,0表示无限循环
实现逻辑:
点亮LED1 // ●○○○○○○○
点亮LED2 // ●●○○○○○○
熄灭LED1,点亮LED3 // ○●●○○○○○
熄灭LED2,点亮LED4 // ○○●●○○○○
... 以此类推
效果特点: 当前LED点亮时,前一个LED保持点亮一个周期后熄灭,形成拖尾效果
9. LED_Effect_BinaryCount() - 二进制计数效果
函数原型:
void LED_Effect_BinaryCount(unsigned int delay_ms, unsigned int loops)
功能描述: 8个LED以二进制方式显示0-255的计数
参数:
delay_ms: 计数延时时间(毫秒)loops: 循环次数,0表示无限循环
实现逻辑:
for i = 0 to 255:
全部熄灭
if (i & 0x01) 点亮LED1 // 第0位
if (i & 0x02) 点亮LED2 // 第1位
if (i & 0x04) 点亮LED3 // 第2位
if (i & 0x08) 点亮LED4 // 第3位
if (i & 0x10) 点亮LED5 // 第4位
if (i & 0x20) 点亮LED6 // 第5位
if (i & 0x40) 点亮LED7 // 第6位
if (i & 0x80) 点亮LED8 // 第7位
延时delay_ms
效果演示:
- 0:
○○○○○○○○ - 1:
●○○○○○○○ - 2:
○●○○○○○○ - 3:
●●○○○○○○ - ...
- 255:
●●●●●●●●
应用场景: 可用于学习二进制计数原理,或作为数字显示
10. LED_Effect_Twinkle() - 随机闪烁效果
函数原型:
void LED_Effect_Twinkle(unsigned int delay_ms, unsigned int loops)
功能描述: 所有LED同时进行状态翻转,形成随机闪烁效果
参数:
delay_ms: 闪烁延时时间(毫秒)loops: 循环次数,0表示无限循环
实现逻辑:
for i = 1 to 8:
翻转LED[i]状态
延时delay_ms
效果特点: 每次循环所有LED同时翻转状态,产生动态闪烁效果
4.3 延时模块 (TF_Delay.c/h)
提供软件延时和SysTick硬件延时两种方式。
4.3.1 软件延时函数
void Delay_nus(unsigned long n)
功能: 微秒级软件延时
参数:
n: 延时时间(单位:微秒),1表示1us,1000表示1ms
实现:
void Delay_nus(unsigned long n)
{
unsigned long j;
while(n--)
{
j=12;
while(j--);
}
}
特点:
- 简单的循环延时实现
- 精度取决于系统时钟和编译优化
- 占用CPU资源
void Delay_nms(unsigned long n)
功能: 毫秒级软件延时
参数:
n: 延时时间(单位:毫秒),1表示1ms,1000表示1s
实现:
void Delay_nms(unsigned long n)
{
while(n--)
Delay_nus(1030); // 调用1030us延时补偿函数调用开销
}
注意: 使用1030us而非1000us是为了补偿函数调用开销
4.3.2 SysTick硬件延时函数
void SysTick_Delay_ms(__IO uint32_t nTime)
功能: 基于SysTick的毫秒级精确延时
参数:
nTime: 延时时间(单位:毫秒)
特点:
- 使用SysTick中断实现
- 延时精度高
- 1ms产生一次SysTick中断
实现原理:
void SysTick_Delay_ms(__IO uint32_t nTime)
{
while(SysTick_Config(SystemCoreClock/1000)); // 配置1ms中断一次
TimingDelay = nTime;
while(TimingDelay != 0); // 等待计数完成
SysTick->CTRL = 0x00; // 关闭计数器
SysTick->VAL = 0x00; // 清空计数器
}
void SysTick_Delay_us(__IO uint32_t nTime)
功能: 基于SysTick的微秒级精确延时
参数:
nTime: 延时时间(单位:微秒)
特点:
- 使用SysTick中断实现
- 1us产生一次SysTick中断
- 适合短时间精确延时
void SysTick_Delay(__IO uint32_t nTime)
功能: 通用SysTick延时函数
参数:
nTime: 延时计数值(配合SysTick_Config使用)
void TimingDelay_Decrement(void)
功能: SysTick中断中调用的计数递减函数
说明: 在SysTick_Handler()中断服务函数中调用,用于递减延时计数器。
实现:
void TimingDelay_Decrement(void)
{
if (TimingDelay != 0x00)
{
TimingDelay--;
}
}
5. 使用示例
5.1 运行所有效果(默认模式)
int main(void)
{
LED_Init();
while(1)
{
// 依次演示10种效果
LED_Effect_Flow(100, 3);
Delay_nms(500);
LED_Effect_Flow_NoOff(100, 2);
Delay_nms(500);
LED_Effect_RunningLight(80, 2);
Delay_nms(500);
LED_Effect_Blink(200, 5);
Delay_nms(500);
LED_Effect_Alternate(150, 5);
Delay_nms(500);
LED_Effect_MiddleSpread(100, 3);
Delay_nms(500);
LED_Effect_Wave(100, 3);
Delay_nms(500);
LED_Effect_Meteor(80, 2);
Delay_nms(500);
LED_Effect_BinaryCount(50, 1);
Delay_nms(500);
LED_Effect_Twinkle(100, 20);
Delay_nms(1000);
}
}
5.2 单一效果持续运行
int main(void)
{
LED_Init();
// 选择一种效果持续运行(无限循环)
LED_Effect_RunningLight(80, 0); // 0表示无限循环
}
5.3 自定义效果组合
int main(void)
{
LED_Init();
while(1)
{
// 快速流水灯
LED_Effect_Flow(50, 5);
Delay_nms(200);
// 慢速跑马灯
LED_Effect_RunningLight(150, 3);
Delay_nms(200);
// 快速闪烁
LED_Effect_Blink(100, 10);
Delay_nms(500);
}
}
5.4 使用基础控制函数自定义效果
int main(void)
{
LED_Init();
uint8_t i;
while(1)
{
// 自定义效果:双向流水灯
for(i = 1; i <= 4; i++)
{
LED_On(i);
LED_On(9-i);
Delay_nms(100);
LED_Off(i);
LED_Off(9-i);
}
Delay_nms(500);
}
}
6. 效果函数参数调优指南
6.1 延时参数建议
| 效果类型 | 推荐延时范围 | 最佳视觉效果 | 说明 |
|---|---|---|---|
| 流水灯 | 50-200ms | 80-100ms | 太快看不清,太慢显得拖沓 |
| 跑马灯 | 50-150ms | 70-90ms | 需要较快速度体现动感 |
| 闪烁 | 100-500ms | 150-250ms | 人眼舒适的闪烁频率 |
| 奇偶交替 | 100-300ms | 150-200ms | 切换不宜过快 |
| 中间扩散 | 80-200ms | 100-120ms | 逐级扩散需要清晰 |
| 波浪 | 50-150ms | 80-100ms | 快速流动更有动感 |
| 流星 | 50-120ms | 70-90ms | 快速才有流星感觉 |
| 二进制计数 | 30-100ms | 40-60ms | 计数太慢会很长时间 |
| 随机闪烁 | 50-200ms | 100-150ms | 适中的闪烁频率 |
6.2 循环次数建议
| 效果类型 | 建议循环次数 | 总时长估算 | 说明 |
|---|---|---|---|
| 流水灯 | 2-5次 | 约2-5秒 | 完整展示效果 |
| 跑马灯 | 2-4次 | 约3-6秒 | 来回一次为一个循环 |
| 闪烁 | 3-10次 | 约1-5秒 | 几次闪烁即可 |
| 奇偶交替 | 3-8次 | 约1-5秒 | 交替几次体现效果 |
| 中间扩散 | 2-5次 | 约2-5秒 | 扩散收缩过程 |
| 波浪 | 2-5次 | 约2-5秒 | 波浪流动过程 |
| 流星 | 1-3次 | 约2-6秒 | 完整流动过程 |
| 二进制计数 | 1-2次 | 约13-26秒 | 0-255需要较长时间 |
| 随机闪烁 | 10-30次 | 约1-6秒 | 多次闪烁才有效果 |
6.3 效果组合建议
模式A:展示模式(默认)
- 适用场景:产品展示、功能演示
- 特点:每种效果运行适中次数,全面展示所有功能
- 总循环时间:约40-60秒
模式B:快速循环模式
LED_Effect_Flow(50, 2);
LED_Effect_RunningLight(60, 1);
LED_Effect_Blink(100, 3);
LED_Effect_Alternate(100, 3);
- 适用场景:商业广告、吸引注意
- 特点:快速切换,节奏紧凑
- 总循环时间:约10-15秒
模式C:单一循环模式
LED_Effect_RunningLight(80, 0); // 无限循环
- 适用场景:简单装饰、持续运行
- 特点:专注单一效果,循环播放
7. 重要注意事项
7.1 JTAG调试接口重映射
- 程序中禁用了JTAG功能,保留了SWD调试接口
- 这使得PB3、PB4引脚可以作为普通GPIO使用
- 调试时请使用SWD接口,不要使用JTAG接口
7.2 时钟配置
- LED驱动使用的是GPIOB,时钟为APB2总线
- 必须在使用GPIO前使能相应的时钟
- 已在
LED_Init()中自动配置
7.3 代码中发现的问题
问题1:LED引脚时钟宏定义错误
位置: TF_LED.h (第33、37、41、45、49、53、57、61行)
问题描述:
#define LED1_CLK RCC_APB2Periph_GPIOC // 错误:应该是GPIOB
#define LED2_CLK RCC_APB2Periph_GPIOC // 错误:应该是GPIOB
// ... LED3~LED8同样的问题
影响: 由于实际使用的是LED_CLK宏(已正确定义为RCC_APB2Periph_GPIOB),所以不影响程序运行。
建议修改:
#define LED1_CLK RCC_APB2Periph_GPIOB
#define LED2_CLK RCC_APB2Periph_GPIOB
#define LED3_CLK RCC_APB2Periph_GPIOB
#define LED4_CLK RCC_APB2Periph_GPIOB
#define LED5_CLK RCC_APB2Periph_GPIOB
#define LED6_CLK RCC_APB2Periph_GPIOB
#define LED7_CLK RCC_APB2Periph_GPIOB
#define LED8_CLK RCC_APB2Periph_GPIOB
7.4 延时函数选择建议
| 场景 | 推荐函数 | 原因 |
|---|---|---|
| 普通延时 | Delay_nms() | 简单易用,不占用外设资源 |
| 精确延时 | SysTick_Delay_ms() | 精度高,不受系统负载影响 |
| 长时间延时 | Delay_nms() | 避免长时间关闭中断 |
| 微秒级延时 | Delay_nus() | 简单快速 |
注意: 本项目所有效果函数都使用Delay_nms(),如需高精度可以修改为SysTick_Delay_ms()
7.5 CPU占用说明
- 所有效果函数都是阻塞式运行
- 运行期间CPU一直处于工作状态
- 如需后台运行,建议使用定时器中断实现
8. 编译和下载
8.1 开发环境
- IDE: Keil MDK-ARM (μVision)
- 编译器: ARMCC
- 调试器: J-Link / ST-Link
- 下载接口: SWD
8.2 工程配置
工程文件位于 MDK/TEST_CODE.uvproj
关键配置:
- Target Device: STM32F103C8
- Crystal: 8.0 MHz
- Use MicroLIB: 推荐开启(减小代码体积)
- Optimization Level: Level 1
8.3 下载步骤
- 打开工程文件
MDK/TEST_CODE.uvproj - 编译工程(F7)
- 连接调试器到开发板的SWD接口
- 下载程序(F8)
- 运行程序,观察LED广告灯效果
8.4 编译输出
编译成功后可在 MDK/Output/ 目录下找到以下文件:
- .hex: 可下载的十六进制文件
- .bin: 二进制文件
- .axf: 调试文件
9. 常见问题
Q1: LED不亮或只有部分亮?
检查项:
- 确认硬件连接正确(PB0~PB7)
- 检查LED极性(共阴/共阳)
- 确认GPIO时钟已使能
- 用万用表测量GPIO引脚电平
- 检查程序是否正确下载
解决方案:
// 在main函数开始添加测试代码
LED_Init();
LED_AllOn(); // 如果都能亮,说明硬件正常
Delay_nms(2000);
LED_AllOff();
Q2: 效果运行速度不正常?
可能原因:
- 系统时钟配置不正确
- 延时参数设置不合理
- 编译优化等级影响延时精度
解决方案:
- 检查
system_stm32f10x.c中的时钟配置 - 调整
delay_ms参数 - 使用SysTick硬件延时提高精度
Q3: 下载失败?
解决方案:
- 检查调试器连接
- 确认使用SWD接口(不要用JTAG)
- 尝试在上电时按住复位键,然后下载
- 检查供电是否正常(3.3V)
- 使用Keil的"Erase Full Chip"功能
Q4: 效果切换时有短暂停顿?
原因: Delay_nms(500) 或 Delay_nms(1000) 间隔时间
解决方案:
// 减少或去除效果间的延时
LED_Effect_Flow(100, 3);
// Delay_nms(500); // 注释掉此行
LED_Effect_Flow_NoOff(100, 2);
Q5: 如何添加自定义效果?
步骤:
- 在
TF_LED.h中添加函数声明:
void LED_Effect_Custom(unsigned int delay_ms, unsigned int loops);
- 在
TF_LED.c中实现函数:
void LED_Effect_Custom(unsigned int delay_ms, unsigned int loops)
{
unsigned int count = 0;
while(loops == 0 || count < loops)
{
// 在这里编写自定义效果逻辑
LED_On(1);
Delay_nms(delay_ms);
LED_Off(1);
count++;
}
}
- 在
main.c中调用:
LED_Effect_Custom(100, 5);
Q6: 如何实现非阻塞式效果?
当前实现: 所有效果函数都是阻塞式的
改进方案: 使用定时器+状态机实现
// 示例:使用定时器中断实现非阻塞流水灯
volatile uint8_t led_index = 1;
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
LED_AllOff();
LED_On(led_index);
led_index++;
if(led_index > 8) led_index = 1;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}