本章源代码链接:
链接: link
1、点亮LED
实验要求:点亮LED
学习使用的寄存器:
①main.c文件的代码如下:
#include "stdint.h"
int main(void)
{
/* 1、开启对应的GPIOA的时钟 */
*(uint32_t *)(0x40021000 + 0x18) = 0x04; //RCC_APB2ENR寄存器的IOPAEN位置1
/* 2、给IO口设置工作模式:PA0配置为通用推挽输出模式 */
*(uint32_t *)(0x40010800 + 0x00) = 0x03; //GPIOA_CRL寄存器的MODE0位置11,CNF0位置00
/* 3、对应的IO口设置值:1/0,PA0输出1,点亮LED */
*(uint32_t *)(0x40010800 + 0x0C) = 0x00001; //GPIOA_ODR寄存器的ODR0位置1
while(1)
{
}
}
综上:
从上面的代码开发步骤中,每次需要配置某个片上外设的寄存器时,都需要先打开存储器映像来找到这个片上外设的寄存器初始地址(基地址),然后通过基地址+偏移地址最终定位到需要的那个寄存器的地址。使用这种开发方式大大降低了我们开发的效率。
那有没有更优化的寄存器开发方式喃?
答案:在stm32f10x.h中已经将每个寄存器映射空间的初始地址都使用宏定义好了,直接调用宏定义即可,且寄存器的每一个对应的位也用宏定义好了,如下图所示:
则优化后的代码如下:
①main.c文件的代码如下:
#include "stm32f10x.h"
int main(void)
{
/* 1、开启对应的GPIOA的时钟 */
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; //RCC_APB2ENR寄存器的IOPAEN位置1,RCC是结构体RCC_TypeDef的地址
/* 2、给IO口设置工作模式:PA0配置为通用推挽输出模式 */
GPIOA->CRL |= GPIO_CRL_MODE0; //GPIOA_CRL寄存器的MODE0位置11,CNF0位置00,GPIOA是结构体GPIO_TypeDef的地址
GPIOA->CRL &= ~GPIO_CRL_CNF0;
/* 3、对应的IO口设置值:1/0,PA0输出1,点亮LED */
GPIOA->ODR |= GPIO_ODR_ODR0; //GPIOA_ODR寄存器的ODR0位置1
while(1)
{
}
}
2、LED闪烁
实验要求:LED每隔1s亮灭
①LED.c文件的代码如下:
#include "LED.h"
/**
* LED引脚初始化
* 参数A:PA0~PA7,PA0 = 0
*/
void LED_Init(GPIO A)
{
/* 1、开启GPIOA的时钟 */
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
/* 2、对引脚工作模式进行配置:通用推挽输出 */
GPIOA->CRL |= (GPIO_CRL_MODE0 << (A*4));
GPIOA->CRL &= ~(GPIO_CRL_CNF0 << (A*4));
}
/**
* 点亮LED
* 参数A:PA0~PA7,PA0 = 0
*/
void LED_ON(GPIO A)
{
/* 点亮LED,引脚输出低电平 */
GPIOA->ODR |= (GPIO_ODR_ODR0 << A);
}
/**
* 熄灭LED
* 参数A:PA0~PA7,PA0 = 0
*/
void LED_OFF(GPIO A)
{
/* 熄灭LED,引脚输出高电平 */
GPIOA->ODR &= ~(GPIO_ODR_ODR0 << A);
}
/**
* 翻转LED
* 参数A:PA0~PA7,PA0 = 0
*/
void LED_Turn(GPIO A)
{
/* 如果LED是点亮 */
if(GPIOA->ODR & (GPIO_ODR_ODR0 << A))//输出引脚为高电平
{
/* 熄灭LED */
LED_OFF(A);
}
else
{
/* 否则点亮LED */
LED_ON(A);
}
}
②LED.h文件的代码如下:
#ifndef __LED_H
#define __LED_H
#include "stm32f10x.h"
/* 使用枚举定义一个GPIO类型*/
typedef enum{
PA0, //PA0 = 0
PA1, //PA1 = 1
PA2,
PA3,
PA4,
PA5,
PA6,
PA7 //PA7 = 7
} GPIO;
void LED_Init(GPIO A);
void LED_ON(GPIO A);
void LED_OFF(GPIO A);
void LED_Turn(GPIO A);
#endif
③Delay.c文件的代码如下:(定时器章节进行讲解)
#include "Delay.h"
/*
* 延迟函数us
*/
void Delay_us(uint16_t us)
{
// 设置 SysTick 的计数周期
SysTick_Config(SystemCoreClock/1000000); // 每周期 1us
// 关闭 SysTick 的中断,不可省略
SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk;
// 开始计数
for(uint32_t i=0; i<us; i++)
{ // 循环一次就是 1us
while( !((SysTick->CTRL)&(1<<16)) );
}
// 关闭 SysTick 定时器
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
/*
* 延迟函数ms
*/
void Delay_ms(uint16_t ms)
{
while (ms--)
{
Delay_us(1000);
}
}
/*
* 延迟函数s
*/
void Delay_s(uint16_t s)
{
while (s--)
{
Delay_ms(1000);
}
}
④Delay.h文件的代码如下:
#ifndef __Delay_h
#define __Delay_h
#include "stm32f10x.h"// Device header
void Delay_us(uint16_t us);
void Delay_ms(uint16_t ms);
void Delay_s(uint16_t s);
#endif
⑤main.c文件的代码如下:
#include "LED.h"
#include "Delay.h"
int main(void)
{
LED_Init(PA0); //初始化PA0引脚
LED_OFF(PA0); //熄灭连接着PA0引脚的LED
while(1)
{
LED_ON(PA0); //点亮连接着PA0引脚的LED
Delay_ms(1000); //延时1000ms
LED_OFF(PA0); //熄灭连接着PA0引脚的LED
Delay_ms(1000); //延时1000ms
}
}
3、LED流水灯
⑤main.c文件的代码如下:
#include "LED.h"
#include "Delay.h"
int main(void)
{
for(uint8_t i = 0; i<4; i++)
{
LED_Init((GPIO)i); //初始化PA0~PA3引脚
}
while(1)
{
for(uint8_t i = 0; i<4; i++)
{
LED_ON((GPIO)i); //点亮LED
LED_OFF((GPIO)((i + 3) % 4)); //熄灭上一个LED
Delay_ms(500);
}
}
}
4、按键控制LED
①Key.c文件的代码如下:
#include "Key.h"
/**
* Key1引脚PB0的初始化
*/
void Key_Init(void)
{
/* 1、开启GPIOB的时钟 */
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
/* 2、对引脚工作模式进行配置:上拉输入模式 */
//MODE0 = 00 ,CNF0 = 10 ,ODR0 = 1
GPIOB->CRL &= ~GPIO_CRL_MODE0;
GPIOB->CRL &= ~GPIO_CRL_CNF0;
GPIOB->CRL |= GPIO_CRL_CNF0_1;
GPIOB->ODR |= GPIO_ODR_ODR0; //表示上拉模式
}
/**
* 检测按键是否按下
*/
uint8_t Key_Num(void)
{
uint8_t num = 0;
if(!(GPIOB->IDR & GPIO_IDR_IDR0)) //如果按键按下
{
Delay_ms(10); //延时消抖
if(!(GPIOB->IDR & GPIO_IDR_IDR0)) //按键真的按下
{
num = 1;
while(!(GPIOB->IDR & GPIO_IDR_IDR0)); //等待按键松开
Delay_ms(10); //延时消抖
}
}
return num;
}
②Key.h文件的代码如下:
#ifndef __Key_H
#define __Key_H
#include "stm32f10x.h"
#include "Delay.h"
void Key_Init(void);
uint8_t Key_Num(void);
#endif
③main.c文件的代码如下:
#include "LED.h"
#include "Key.h"
int main(void)
{
LED_Init(PA0);
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、开启GPIOC的时钟 */
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
/* 2、对引脚工作模式进行配置:通用推挽输出 */
//MODE1 = 11 ,CNF1 = 00
GPIOC->CRL |= GPIO_CRL_MODE1;
GPIOC->CRL &= ~GPIO_CRL_CNF1;
}
/**
* 蜂鸣器响
*/
void Buzzer_ON(void)
{
/* PC1输出低电平 */
GPIOC->ODR &= ~GPIO_ODR_ODR1;
}
/**
* 蜂鸣器不响
*/
void Buzzer_OFF(void)
{
/* PC1输出高电平 */
GPIOC->ODR |= GPIO_ODR_ODR1;
}
②Buzzer.h文件的代码如下:
#ifndef __Buzzer_H
#define __Buzzer_H
#include "stm32f10x.h"
void Buzzer_Init(void);
void Buzzer_ON(void);
void Buzzer_OFF(void);
#endif
③main.c文件的代码如下:
#include "Buzzer.h"
#include "Delay.h"
int main(void)
{
Buzzer_Init();
while(1)
{
Buzzer_ON(); //响
Delay_ms(200);
Buzzer_OFF(); //不响
Delay_ms(200);
}
}