实验1 流水灯控制实验一、实验目的掌握STM32 GPIO寄存器底层原理理解GPIO端口寄存器地址、配置参数。掌握寄存器方式编写GPIO输出驱动实现LED流水灯。学会STM32标准外设库工程建立、库文件添加方法标准库方式驱动LED。掌握HAL库外部中断实现按键控制流水灯暂停/继续。学会Keil软件仿真逻辑分析仪观测GPIO输出波形测量闪烁周期。二、实验环境硬件STM32F103C8T6最小系统板、3只红绿蓝LED、杜邦线、面包板软件Keil MDK5STM32标准外设库V3.5STM32CubeMXHAL库ST-Link下载器三、实验原理STM32F103的GPIO端口可配置为通用推挽输出模式输出高低电平控制LED亮灭。LED一端串联电阻接GPIO引脚另一端接GNDGPIO输出低电平LED点亮输出高电平LED熄灭。最小系统自带PC13LED原理一致PC13为推挽输出控制板载LED。GPIO关键寄存器以GPIOC为例GPIOC_CRL端口低8位配置寄存器引脚0~7GPIOC_CRH端口高8位配置寄存器引脚8~15PC13在此寄存器GPIOC_ODR端口输出数据寄存器置1输出高电平置0输出低电平RCC_APB2ENRAPB2外设时钟使能寄存器开启GPIO端口时钟任务1寄存器方式实现流水灯包含PC13板载LED1.1 程序设计思路开启GPIOA、GPIOB、GPIOC的APB2外设时钟。配置GPIOA、GPIOB、GPIOC对应LED引脚为推挽输出模式最大速度50MHz。循环依次点亮单个LED其余熄灭软件延时1s不断轮换实现流水灯。将板载PC13 LED加入流水灯序列与外接3个LED轮流闪烁。寄存器地址RCC基地址0x40021000RCC_APB2ENR偏移0x18GPIOA基地址0x40010800GPIOB基地址0x40010C00GPIOC基地址0x40011000寄存器偏移CRL0x00CRH0x04ODR0x0C1.2 寄存器版C代码带详细注释#includestm32f10x.h// 简单软件延时函数粗略延时1svoidDelay_s(uint32_ts){uint32_ti,j;for(i0;is;i)for(j0;j7200000;j);}voidLED_GPIO_Init(void){// 1.开启GPIOA/GPIOB/GPIOC时钟 RCC_APB2ENR*(volatileuint32_t*)(0x400210000x18)|(12)|(13)|(14);// bit2:GPIOA, bit3:GPIOB, bit4:GPIOC// PA0推挽输出 50MHzCRL寄存器*(volatileuint32_t*)(0x400108000x00)~(0x0F0);//清空PA0*(volatileuint32_t*)(0x400108000x00)|(0x030);//0011 推挽输出50M// PB0推挽输出 50MHz*(volatileuint32_t*)(0x40010C000x00)~(0x0F0);*(volatileuint32_t*)(0x40010C000x00)|(0x030);// PC13推挽输出 50MHzCRH寄存器(PC13对应bit20~23)*(volatileuint32_t*)(0x400110000x04)~(0x0F20);*(volatileuint32_t*)(0x400110000x04)|(0x0320);}intmain(void){LED_GPIO_Init();while(1){// PA0亮其余灭*(volatileuint32_t*)(0x400108000x0C)~(10);*(volatileuint32_t*)(0x40010C000x0C)|(10);*(volatileuint32_t*)(0x400110000x0C)|(113);Delay_s(1);// PB0亮其余灭*(volatileuint32_t*)(0x400108000x0C)|(10);*(volatileuint32_t*)(0x40010C000x0C)~(10);*(volatileuint32_t*)(0x400110000x0C)|(113);Delay_s(1);// PC13板载LED亮其余灭*(volatileuint32_t*)(0x400108000x0C)|(10);*(volatileuint32_t*)(0x40010C000x0C)|(10);*(volatileuint32_t*)(0x400110000x0C)~(113);Delay_s(1);}}任务2标准外设库版本流水灯2.1工程项目创建、添加标准库详细步骤1.打开 Keil MDK5新建Project选择芯片STM32F103C8T6创建工程文件夹命名LED_STDLIB。2.从 STM32F10x_StdPeriph_Lib 库中复制inc头文件文件夹、src源文件文件夹到工程目录。3.Keil 工程中点击Manage Project Items新建StdLib分组添加需要的库.c文件stm32f10x_gpio.c、stm32f10x_rcc.c。4.打开OptionsforTarget → C/C添加头文件路径..\inc添加宏定义STM32F10X_MD,USE_STDPERIPH_DRIVER。5.新建main.c添加用户代码编译工程。2.2标准库代码#includestm32f10x.hvoidDelay_s(uint32_ts){uint32_ti,j;for(i0;is;i)for(j0;j7200000;j);}voidLED_Init(void){GPIO_InitTypeDef GPIO_InitStruct;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);//PA0GPIO_InitStruct.GPIO_PinGPIO_Pin_0;GPIO_InitStruct.GPIO_ModeGPIO_Mode_Out_PP;GPIO_InitStruct.GPIO_SpeedGPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_InitStruct);//PB0GPIO_InitStruct.GPIO_PinGPIO_Pin_0;GPIO_Init(GPIOB,GPIO_InitStruct);//PC13GPIO_InitStruct.GPIO_PinGPIO_Pin_13;GPIO_Init(GPIOC,GPIO_InitStruct);}intmain(void){LED_Init();while(1){GPIO_ResetBits(GPIOA,GPIO_Pin_0);GPIO_SetBits(GPIOB,GPIO_Pin_0);GPIO_SetBits(GPIOC,GPIO_Pin_13);Delay_s(1);GPIO_SetBits(GPIOA,GPIO_Pin_0);GPIO_ResetBits(GPIOB,GPIO_Pin_0);GPIO_SetBits(GPIOC,GPIO_Pin_13);Delay_s(1);GPIO_SetBits(GPIOA,GPIO_Pin_0);GPIO_SetBits(GPIOB,GPIO_Pin_0);GPIO_ResetBits(GPIOC,GPIO_Pin_13);Delay_s(1);}}2.3软件延时周期Keil 逻辑分析仪仿真1.软件循环延时特点软件空循环延时不精确会受编译器优化、CPU 时钟影响只是粗略1秒。2.Keil 仿真步骤-开启 DebugUse Simulator进入 Debug 模式。-打开Logic Analyzer添加 GPIOA-ODR、GPIOB-ODR、GPIOC-ODR 信号。-运行仿真观测波形高低电平翻转时刻。3.时序分析 逻辑分析仪可以捕获 GPIO 引脚电平跳变测量高电平持续时间、低电平持续时间。 软件延时的实际周期不等于1s存在误差可根据波形读出真实闪烁周期。 任务3HAL 库外部中断按键控制流水灯暂停/继续#includestm32f1xx_hal.h//全局标志uint8_trun_flag1;//1运行0暂停GPIO_HandleTypeDef hgpio_led;GPIO_HandleTypeDef hgpio_key;voidSystemClock_Config(void);staticvoidMX_GPIO_Init(void);voidEXTI0_IRQHandler(void){HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);}//外部中断回调函数voidHAL_GPIO_EXTI_Callback(uint16_tGPIO_Pin){if(GPIO_PinGPIO_PIN_0){run_flag!run_flag;//翻转启停状态}}voidDelay_ms(uint32_tms){HAL_Delay(ms);}intmain(void){HAL_Init();SystemClock_Config();MX_GPIO_Init();while(1){if(run_flag1){HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);Delay_ms(1000);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);Delay_ms(1000);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);Delay_ms(1000);}}}staticvoidMX_GPIO_Init(void){GPIO_InitTypeDef GPIO_InitStruct{0};__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();__HAL_RCC_GPIOC_CLK_ENABLE();//LED输出GPIO_InitStruct.PinGPIO_PIN_0;GPIO_InitStruct.ModeGPIO_MODE_OUTPUT_PP;GPIO_InitStruct.SpeedGPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOA,GPIO_InitStruct);HAL_GPIO_Init(GPIOB,GPIO_InitStruct);GPIO_InitStruct.PinGPIO_PIN_13;HAL_GPIO_Init(GPIOC,GPIO_InitStruct);//按键PA0 外部中断下降沿GPIO_InitStruct.PinGPIO_PIN_0;GPIO_InitStruct.ModeGPIO_MODE_IT_FALLING;GPIO_InitStruct.PullGPIO_PULLUP;HAL_GPIO_Init(GPIOA,GPIO_InitStruct);HAL_NVIC_EnableIRQ(EXTI0_IRQn);HAL_NVIC_SetPriority(EXTI0_IRQn,0,0);}voidSystemClock_Config(void){RCC_OscInitTypeDef RCC_OscInitStruct{0};RCC_ClkInitTypeDef RCC_ClkInitStruct{0};RCC_OscInitStruct.OscillatorTypeRCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEStateRCC_HSE_ON;RCC_OscInitStruct.HSEPredivValueRCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.HSIStateRCC_HSI_ON;RCC_OscInitStruct.PLL.PLLStateRCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSourceRCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMULRCC_PLL_MUL9;if(HAL_RCC_OscConfig(RCC_OscInitStruct)!HAL_OK){Error_Handler();}RCC_ClkInitStruct.ClockTypeRCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSourceRCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDividerRCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDividerRCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDividerRCC_HCLK_DIV1;if(HAL_RCC_ClockConfig(RCC_ClkInitStruct,FLASH_LATENCY_2)!HAL_OK){Error_Handler();}}voidError_Handler(void){__disable_irq();while(1){}}#ifdefUSE_FULL_ASSERTvoidassert_failed(uint8_t*file,uint32_tline){}#endif/* USE_FULL_ASSERT */四、实验结果与分析1.(https://i-blog.csdnimg.cn/direct/37bf46c6098f4511b3471e99dd40caca.png)(https://i-blog.csdnimg.cn/direct/735b791fd29e4e8eb518df89869671cc.png)寄存器版本程序下载后PA0、PB0、PC13 板载 LED 轮流点亮间隔约 1s流水灯正常运行验证寄存器配置正确。2.标准库版本调用库函数初始化 GPIO实现相同流水效果Keil 逻辑分析仪观测 GPIO 波形高低电平翻转对应 LED 亮灭。软件空循环延时存在误差实际周期和理论1s 有偏差。3.HAL 库外部中断版本按下按键流水灯暂停再次按下恢复流水实现启停控制。(https://i-blog.csdnimg.cn/direct/564a56fccc6f40e8a25afa308a438ca2.png)(https://i-blog.csdnimg.cn/direct/c852b53fa86140dbb688ca8f973d3657.png)(https://i-blog.csdnimg.cn/direct/ebfb093e04cc4b8891f878bffdef447d.png)五、实验总结5.寄存器方式直接操作内存地址底层可控适合理解 STM32 硬件原理代码简洁但可读性差。6.标准外设库封装底层寄存器开发更快工程搭建需要手动添加库文件。7.HAL 库更抽象结合外部中断可以实现按键事件驱动中断不会阻塞主循环。8.软件延时精度低精确定时推荐使用定时器中断Keil 逻辑分析仪可以无硬件示波器情况下观测 GPIO 输出波形调试 GPIO 时序。