流水灯实验(试用第一弹)
LED灯实验堪称单片机界的hello world(借用他人语)。下面来用Nucleo-F412进行第一弹测试--流水灯实验。
板载3颗3色LED,如果只点亮一个灯,感觉有点浪费,呵呵。索性就来一个流水灯吧。下面是硬件连接:
LD1------PB0
LD2------PB7
LD3------PB14


第一步首先建立好工程,个人喜欢移植,虽然可以直接用CubeMX生成工程。新建工程目录如下:

app: 应用程序层 bsp:外围驱动
CMSIS和STM32F4xx_HAL_Driver直接从官方库拷贝,注意需把stm32f4xx_hal_msp_template.c,stm32f4xx_hal_timebase_rtc_alarm_template.c,stm32f4xx_hal_timebase_rtc_wakeup_template.c,stm32f4xx_hal_timebase_tim_template.c这几个文件删除,如果加入到工程中会编译通不过,这里不研究这几个文件的具体作用。
sys:系统框架层,这里主要实现系统运行相关的代码,主要是裸机代码,后续会在此文件夹内实现合作式调度器。
user:main.c以及中断入口文件在此文件夹 EWARM: IAR工程目录
接着建立IAR工程,工程结构如下:

设置头文件路径,及一些宏定义

第二步,代码分析
首先看一下main函数中的实现结构
int main(void)
{
HAL_Init();
/* Configure the system clock to 100 MHz */
SystemClock_Config();
/* Add your application code here
*/
disp_init();
/* Infinite loop */
while (1)
{
disp_task();
}
}
接下来编写led的bsp驱动,以及显示程序,这里使用一个数组管理LED的各个IO的对应关系
GPIO_TypeDef * LED_PORT[LEDn]={LED1_GPIO_PORT, LED2_GPIO_PORT, LED3_GPIO_PORT};
const UINT16_T LED_PIN[LEDn]={LED1_GPIO_PIN, LED2_GPIO_PIN, LED3_GPIO_PIN};
LED IO口初始化,3个灯都是高电平亮,所以初始化为推挽输出,输出低电平,默认不亮。
void led_init(Led_TypeDef led){
GPIO_InitTypeDef GPIO_InitStruct;
LED_GPIO_CLK_ENABLE(led);
GPIO_InitStruct.Pin = LED_PIN[led];
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
HAL_GPIO_Init(LED_PORT[led], &GPIO_InitStruct);
HAL_GPIO_WritePin(LED_PORT[led], LED_PIN[led],GPIO_PIN_RESET);
}
下面是led灯的亮灭和翻转。
void led_on(Led_TypeDef led){
HAL_GPIO_WritePin(LED_PORT[led], LED_PIN[led], GPIO_PIN_SET);
}
void led_off(Led_TypeDef led){
HAL_GPIO_WritePin(LED_PORT[led], LED_PIN[led], GPIO_PIN_RESET);
}
void led_toggle(Led_TypeDef led){
HAL_GPIO_TogglePin(LED_PORT[led],LED_PIN[led]);
}
进行显示前的初始化
void disp_init(void){
led_init(LED1);
led_init(LED2);
led_init(LED3);
leds = 0;
led_bits = 0;
}
leds是一个保存led状态的全局变量,每一位对应一个led灯的状态,这里只用里bit0,1,2三位。led_bits是用来实现led灯的流水灯效果。disp_stream实现了led流水效果的函数。
void disp_stream(void){
leds = (1<<led_bits);
led_bits++;
if(led_bits>=LEDn){
led_bits = 0;
}
}
disp_update更新led的显示状态
void disp_update(void){
UINT8_T i;
UINT16_T temp = leds;
for(i=0;i<LEDn;i++){
if(temp&0x01){
led_on((Led_TypeDef)i);
}else{
led_off((Led_TypeDef)i);
}
temp>>=1;
}
}
每500ms更改以此led灯的状态,程序运行后可以看到led灯循环移动的效果,500ms移动一次
void disp_task(void){
disp_stream();
disp_update();
delay_ms(500);
}
修改一点也得审核,看来我的等级太低了
