本章源代码链接:
链接: link
1、点亮LED
实验要求:点亮LED
①RCC_Init.c文件的代码如下:
/****************************************************/
/*****************此文件是配置时钟源函数****************/
/****************************************************/
#include "RCC_Init.h"
/**
* 将时钟源配置配置为HSI,且AHB,ABP1和APB2的时钟频率到达最大值64MHz,32MHz,64MHz
* HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct)
* HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t FLatency)
*
*/
void HSI_RCC_Init(void)
{
/* 1、配置振荡器HSI和PLL */
/* 1.1、配置振荡器HSI */
RCC_OscInitTypeDef CC_OscInitStruct;
CC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; //选择HSI振荡器作为时钟源
CC_OscInitStruct.HSIState = RCC_HSI_ON; //开启HSI振荡器
CC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; //HSI微调
/* 1.2、配置PLL */
CC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL16; //PLL的倍频系数
CC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2; //选择分频后的HSI
CC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; //开启PLL
HAL_RCC_OscConfig(&CC_OscInitStruct);
/* 2、配置系统时钟 */
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK |
RCC_CLOCKTYPE_HCLK |
RCC_CLOCKTYPE_PCLK1 |
RCC_CLOCKTYPE_PCLK2; //需要配置的总线时钟
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; //系统时钟的输入源
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; //系统时钟的分频系数
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; //APB1的分频系数
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; //APB2的分频系数
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2); //传入配置的结构体指针
//配置Flash的2个等待周期
}
/**
* 将时钟源配置配置为HSE,且AHB,ABP1和APB2的时钟频率到达最大值72MHz,36MHz,72MHz
*/
void HSE_RCC_Init(void)
{
/* 1、配置振荡器HSE和PLL */
RCC_OscInitTypeDef CC_OscInitStruct;
CC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; //选择HSE振荡器作为时钟源
CC_OscInitStruct.HSEState = RCC_HSE_ON; //开启HSE:8MHz
CC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; //HSE的预分配系数:1分频
CC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; //PLL的倍频系数: 9倍频
CC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; //选择HSE作为PLL的来源
CC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; //开启PLL
HAL_RCC_OscConfig(&CC_OscInitStruct);
/* 2、配置系统时钟 */
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK |
RCC_CLOCKTYPE_HCLK |
RCC_CLOCKTYPE_PCLK1 |
RCC_CLOCKTYPE_PCLK2; //需要配置的总线时钟
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; //系统时钟的输入源: PLL
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; //系统时钟的分频系数: 1分频
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; //APB1的分频系数: 2分频
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; //APB2的分频系数: 1分频
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2); //配置Flash的2个等待周期
}
②RCC_Init.h文件的代码如下:
#ifndef __RCC_Init_H
#define __RCC_Init_H
#include "stm32f1xx_hal.h"
void HSI_RCC_Init(void); //选择内部高速时钟源
void HSE_RCC_Init(void); //选择外部高速时钟源
#endif
③LED.c文件的代码如下:
#include "LED.h"
/**
* LED引脚初始化
*/
/* 定义数组,想要添加引脚向里面添加即可 */
uint16_t GPIO_A[] = {PA0,PA1,PA2,PA3};
static uint8_t GPIONum = sizeof(GPIO_A)/sizeof(GPIO_A[0]);
void LED_Init(void)
{
/* 1、开启GPIOA的时钟 */
__HAL_RCC_GPIOA_CLK_ENABLE(); //这里使用的时宏,宏就是直接操作寄存器,常用于开关
/* 2、对GPIOA0进行配置 */
GPIO_InitTypeDef GPIO_Init;
for(uint8_t i = 0; i < GPIONum; i++)
{
GPIO_Init.Pin = GPIO_PIN_0; //选择PA0
GPIO_Init.Mode = GPIO_MODE_OUTPUT_PP; //选择通用推挽输出
//若使用通用开漏输出,则为GPIO_MODE_OUTPUT_OD
GPIO_Init.Speed = GPIO_SPEED_FREQ_HIGH; //最大输出速度50MHz
HAL_GPIO_Init(GPIOA, &GPIO_Init);
}
}
/**
* 点亮LED
* PAx:PA0~PA3
*/
void LED_ON(uint16_t PAx)
{
/* 点亮LED,PAx引脚输出高电平 */
HAL_GPIO_WritePin(GPIOA, PAx, GPIO_PIN_SET);
}
/**
* 熄灭LED
* PAx:PA0~PA3
*/
void LED_OFF(uint16_t PAx)
{
/* 熄灭LED,PAx引脚输出低电平 */
HAL_GPIO_WritePin(GPIOA, PAx, GPIO_PIN_RESET);
}
/**
* 翻转LED
* PAx:PA0~PA3
*/
void LED_Turn(uint16_t PAx)
{
/* 翻转引脚PAx */
HAL_GPIO_TogglePin(GPIOA, PAx);
}
④LED.h文件的代码如下:
#ifndef __LED_H
#define __LED_H
#include "stm32f1xx_hal.h"
#define PA0 GPIO_PIN_0
#define PA1 GPIO_PIN_1
#define PA2 GPIO_PIN_2
#define PA3 GPIO_PIN_3
void LED_Init(void);
void LED_ON(uint16_t PAx);
void LED_OFF(uint16_t PAx);
void LED_Turn(uint16_t PAx);
#endif
⑤Delay.c文件的代码如下:(定时器章节进行讲解)
#include "Delay.h"
/**
* DWT的CYCCNT寄存器的初始化
*/
void DelayInit(void)
{
/* 1、关闭TRCENA寄存器 */
CoreDebug->DEMCR &= ~CoreDebug_DEMCR_TRCENA_Msk;
/* 2、打开TRCENA寄存器 */
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
/* 3、关闭CYCCNT计数功能 */
DWT->CTRL &= ~DWT_CTRL_CYCCNTENA_Msk;
/* 4、打开CYCCNT计数功能 */
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
/* 5、CYCCNT计数清零 */
DWT->CYCCNT = 0;
}
/**
* 微妙延时函数
* CYCCNT寄存器每隔(1/72MHz)s中的数值+1
*/
void Delay_us(uint32_t nUs)
{
uint32_t tickStart = DWT->CYCCNT; //记录一开始CYCCNT寄存器的值
/* 将nUs转换为CYCCNT寄存器对应的值*/
//每隔(1000000/SystemCoreClock)us,寄存器的值+1。每隔1us,寄存器的值 + (SystemCoreClock / 1000000)
uint32_t nUsToTick;
nUsToTick = nUs * (SystemCoreClock / 1000000);
/* 延时等待 */
while ((DWT->CYCCNT - tickStart) < nUsToTick);
}
/**
* 毫秒延时
*/
void Delay_ms(uint32_t nMs)
{
while (nMs--)
{
Delay_us(1000);
}
}
/**
* 秒延时
*/
void Delay_s(uint16_t s)
{
while (s--)
{
Delay_ms(1000);
}
}
⑥Delay.h文件的代码如下:
#ifndef __Delay_H
#define __Delay_H
#include "stm32f1xx_hal.h"
void DelayInit(void);
void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
void Delay_s(uint16_t s);
#endif
⑦main.c文件的代码如下:
#include "RCC_Init.h"
#include "LED.h"
int main(void)
{
HAL_Init(); //初始化
HSE_RCC_Init(); //配置时钟树,配置为最高频率
LED_Init(); //PA0~PA3引脚初始化
LED_ON(PA0); //点亮LED0
while(1)
{
}
}
2、LED闪烁
实验要求:LED每隔1s亮灭
⑤main.c文件的代码如下:
#include "RCC_Init.h"
#include "LED.h"
#include "Delay.h"
int main(void)
{
HAL_Init(); //初始化
HSE_RCC_Init(); //配置时钟树,配置为最高频率
LED_Init(); //PA0~PA3引脚从初始化
DelayInit(); //延时初始化
while(1)
{
LED_Turn(PA0);
Delay_ms(500);
}
}
3、LED流水灯
⑤main.c文件的代码如下:
#include "RCC_Init.h"
#include "LED.h"
#include "Delay.h"
extern uint16_t GPIO_A[];
int main(void)
{
HAL_Init(); //初始化
HSE_RCC_Init(); //配置时钟树,配置为最高频率
LED_Init(); //PA0~PA3引脚从初始化
DelayInit(); //延时初始化
while(1)
{
for(uint8_t i = 0; i < 4; i++)
{
LED_ON(GPIO_A[i]); //点亮
LED_OFF(GPIO_A[(i+3)%4]); //熄灭
Delay_ms(500);
}
}
}
4、按键控制LED
①Key.c文件的代码如下:
#include "Key.h"
/**
* Key1引脚PB0的初始化
*/
void Key_Init(void)
{
/* 1、开启GPIOB的时钟 */
__HAL_RCC_GPIOB_CLK_ENABLE(); //这里使用的时宏,宏就是直接操作寄存器,常用于开关
/* 2、对引脚工作模式进行配置:上拉输入模式 */
GPIO_InitTypeDef GPIO_Init;
GPIO_Init.Pin = GPIO_PIN_0; //选择PB0
GPIO_Init.Pull = GPIO_PULLUP; //上拉模式
GPIO_Init.Mode = GPIO_MODE_INPUT; //选择输入模式
HAL_GPIO_Init(GPIOB, &GPIO_Init);
}
/**
* 检测按键是否按下
*/
uint8_t Key_Num(void)
{
uint8_t num = 0;
if(!HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0)) //如果按键按下
{
Delay_ms(10); //延时消抖
if(!HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0)) //按键真的按下
{
while(!!HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0)); //等待按键松开
Delay_ms(10); //延时消抖
num = 1;
}
}
return num;
}
②Key.h文件的代码如下:
#ifndef __Key_H
#define __Key_H
#include "stm32f1xx_hal.h"
#include "Delay.h"
void Key_Init(void);
uint8_t Key_Num(void);
#endif
③main.c文件的代码如下:
#include "RCC_Init.h"
#include "LED.h"
#include "Key.h"
int main(void)
{
HAL_Init(); //初始化
HSE_RCC_Init(); //配置时钟树,配置为最高频率
LED_Init(); //PA0~PA3引脚从初始化
Key_Init();
while(1)
{
if(Key_Num() == 1)
{
LED_Turn(PA0);
}
}
}
5、蜂鸣器
😀蜂鸣器模块的IO引脚输入低电平时,蜂鸣器响;IO引脚输入高电平时,蜂鸣器不响。
图蜂鸣器IO连接着单片机的PC1引脚,PC1输出低电平,蜂鸣器响,输出高电平,蜂鸣器不响
①Buzzer.c文件的代码如下:
#include "Buzzer.h"
/**
* 蜂鸣器引脚的初始化
*/
void Buzzer_Init(void)
{
/* 1、开启GPIOA的时钟 */
__HAL_RCC_GPIOC_CLK_ENABLE(); //这里使用的时宏,宏就是直接操作寄存器,常用于开关
/* 2、对引脚工作模式进行配置:通用推挽输出 */
GPIO_InitTypeDef GPIO_Init;
GPIO_Init.Pin = GPIO_PIN_1; //选择PC1
GPIO_Init.Mode = GPIO_MODE_OUTPUT_PP; //选择通用推挽输出
//若使用通用开漏输出,则为GPIO_MODE_OUTPUT_OD
GPIO_Init.Speed = GPIO_SPEED_FREQ_HIGH; //最大输出速度50MHz
HAL_GPIO_Init(GPIOC, &GPIO_Init);
}
/**
* 蜂鸣器响
*/
void Buzzer_ON(void)
{
/* PC1输出低电平 */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_RESET);
}
/**
* 蜂鸣器不响
*/
void Buzzer_OFF(void)
{
/* PC1输出高电平 */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_SET);
}
②Buzzer.h文件的代码如下:
#ifndef __Buzzer_H
#define __Buzzer_H
#include "stm32f1xx_hal.h"
void Buzzer_Init(void);
void Buzzer_ON(void);
void Buzzer_OFF(void);
#endif
③main.c文件的代码如下:
#include "RCC_Init.h"
#include "Buzzer.h"
#include "Delay.h"
int main(void)
{
HAL_Init(); //初始化
HSE_RCC_Init(); //配置时钟树,配置为最高频率
Buzzer_Init();
DelayInit();
while(1)
{
Buzzer_ON(); //响
Delay_ms(200);
Buzzer_OFF(); //不响
Delay_ms(200);
}
}