03、GPIO外设(二):寄存器代码示例

本章源代码链接:

链接: 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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值