1、重做上一个LED流水灯作业,即用GPIO端口完成3只LED红绿灯的周期闪烁。
-
安装STM32CubeMX和Keil:首先在计算机上安装STM32CubeMX和Keil软件并配置好。
-
创建一个新的项目:打开STM32CubeMX,选择你使用的STM32系列芯片,然后创建一个新的工程。
-
配置GPIO:在Pinout & Configuration选项卡中,选择你要使用的GPIO引脚,并将它们配置为输出模式。
-
配置时钟和中断:在RCC选项卡中,配置系统时钟和外部中断使能。你可以选择内部时钟源或外部时钟源,取决于你的具体需求。
-
配置中断优先级和触发方式:在NVIC选项卡中,为外部中断通道分配优先级,并选择触发方式(上升沿触发、下降沿触发等)。
-
生成代码:点击"Project"选项卡,选择你的开发环境(Keil),然后点击"Generate Code"生成代码。
-
打开Keil并导入项目:打开Keil软件,然后导入由STM32CubeMX生成的项目。
-
编写代码:在Keil中,打开main.c文件,编写你的代码来控制LED的闪烁。你可以使用HAL库提供的函数来控制GPIO引脚的状态和切换。
-
构建和下载代码:在Keil中,点击构建按钮来编译你的代码,并将生成的hex或bin文件下载到STM32开发板上。
使用HAL库函数控制GPIO闪烁LED的如下代码,可以实现3只LED红绿灯的周期闪烁:
#include "main.h"
#include "stm32f1xx_hal.h"
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
while (1)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7);
HAL_Delay(500);
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL16;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / 1000);
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
void Error_Handler(void)
{
while (1)
{
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif
2、用stm32F103核心板的GPIOA端某一管脚接一个开关(用杜邦线模拟代替)。采用中断模式编程,当开关接高电平时,LED流水灯工作;
接低电平时,LED流水灯停止工作。
基于STM32F103核心板的GPIO中断模式编程,代码如下:
#include "stm32f10x.h"
void GPIO_Configuration(void);
void EXTI_Configuration(void);
void LED_Flowing(void);
int main(void)
{
GPIO_Configuration();
EXTI_Configuration();
while (1)
{
// 主循环中的其他任务
}
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE);
// 配置LED流水灯引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
// 配置开关引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 假设开关接在GPIOA的Pin_0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入模式
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void EXTI_Configuration(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 配置开关引脚触发中断
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
// 配置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 抢占优先级为0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 子优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void EXTI0_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line0) != RESET)
{
// 延时一段时间以消除抖动
int delay = 100000;
while(delay--);
// 检查开关引脚电平
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET)
{
// 低电平,停止LED流水灯
GPIO_ResetBits(GPIOC, GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10);
}
else
{
// 高电平,启动LED流水灯
LED_Flowing();
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
void LED_Flowing(void)
{
// 实现LED的流水灯效果
int i;
for (i = 0; i < 3; i++)
{
GPIO_SetBits(GPIOC, GPIO_Pin_8 << i);
DELAY_ms(500);
GPIO_ResetBits(GPIOC, GPIO_Pin_8 << i);
}
}
实验过程图:
二. 在没有示波器条件下,可以使用Keil的软件仿真逻辑分析仪功能观察管脚的时序波形,更方便动态跟踪调试和定位代码故障点。
请用此功能观察第1题中3个GPIO端口流水灯的输出波形,并分析时序状态正确与否,高低电平转换周期(LED闪烁周期)实际为多少。
根据第1题的代码,LED流水灯的输出波形是在一个循环中依次亮起和熄灭。通过观察GPIOC的Pin_8、Pin_9和Pin_10的状态变化,您可以确定高低电平转换的周期。
LED闪烁周期取决于LED_Flowing()函数中延时的时间,该示例代码中设定为500毫秒。因此,LED闪烁周期实际上为0.5秒。
使用Keil软件仿真逻辑分析仪功能来验证时序状态的正确性和实时观察GPIO引脚的状态变化。通过观察波形,您可以确认LED流水灯按照预期的方式在不同的GPIO端口上闪烁,并且高低电平转换的频率是正确的。