实验1 : GPIO接口实验

1、分析LED1灯电路图

1. 分析电路图的连接关系,操作的外设(比如:LED,KEY)连接到SOC的哪个GPIO引脚。
2. 分析控制外设电路图的工作的原理,比如LED灯的工作的原理。

1.1 分析电路图的连接关系

灯的网络标识,LED1

image-20230714133558493

在根据原理图找到LED1,LED2,LED3连接的GPIO引脚,后面配置的时候就配置相应的组

image-20230714133839795

1.2 分析电路图的工作原理

image-20230714141832698

三极管的分类:
	NPN三极管   PNP三极管

三极管的状态:
	放大,饱和,截止

image-20230714142042927

	NPN : 基极为高电平时,集电极和发射极导通;
		基极为低电平时,集电极和发射极截止。
		
	PNP : 基极为高电平时,集电极和发射极截止;
		基极为低电平时,集电极和发射极导通。
LED1灯的工作原理:
	PE10引脚输出高电平时,NPN三极管导通,LED1灯亮;
	PE10引脚输出低电平时,NPN三极管截止,LED1灯灭。
	
编写驱动程序,控制PE10引脚输出高电平,LED1灯亮;
	控制PE10引脚输出低电平,LED1灯灭;

2、分析芯片手册

1. 如何编写驱动程序,控制对应的外设工作。
2. 掌握如何分析芯片手册,以及分析芯片手册的技巧。

  

image-20230714150430529

2.1 分析GPIO章节

image-20230714154652912

上下拉电阻:

image-20230714155355020

GPIO外设控制器框图:

image-20230714155837192

MOS管的工作原理:

推挽输出和开漏输出:

image-20230628103428518

mos管具有开关的特性
	NMOS : 栅极为高电平时,源极和漏极导通
			栅极为低电平时,源极和漏极截止
	PMOS : 栅极为高电平时,源极和漏极截止
			栅极为低电平时,源极和漏极导通

推挽输出:

image-20230714160407949

开漏输出:

image-20230714160633294

image-20230714161315067

编程的思路:以LED1灯为例 ---> PE10引脚
// PE10引脚的初始化操作
    配置GPIOE_MODER寄存器,配置PE10引脚为输出模式;
    配置GPIOE_OTYPER寄存器,配置PE10引脚为推挽输出;
    配置GPIOE_OSPEEDR寄存器,配置PE10引脚的速度;
    配置GPIOE_PUPDR寄存器,配置PE10引脚的上下拉电阻;

// 配置PE10引脚输出高低电平
	配置GPIOE_ODR寄存器,配置PE10引脚输出高电平
	配置GPIOE_ODR寄存器,配置PE10引脚输出低电平

2.1.1 配置GPIOE_MODER寄存器,配置PE10引脚为输出模式;

image-20230714162436942

2.1.2 配置GPIOE_OTYPER寄存器,配置PE10引脚为推挽输出;

image-20230714164402460

2.1.3 配置GPIOE_OSPEEDR寄存器,配置PE10引脚的速度;

image-20230714164648676

2.1.4 配置GPIOE_PUPDR寄存器,配置PE10引脚的上下拉电阻;

image-20230714164855318

2.1.5 配置GPIOE_ODR寄存器,配置PE10引脚输出高低电平

image-20230714165118928

2.2 分析2.5.2章节,确定GPIOE外设接到哪根总线上,以及外设寄存器对应的基地址

image-20230714165504593

2.3 分析RCC章节

image-20230714165840920

image-20230714170011814

3、编写驱动代码

 led.c

#include "../include/led.h"

void led_init(void){
    //led1初始化
    //使能led1时钟
    *RCC_MP_AHB4ENSETR &=~(1<<4);
    *RCC_MP_AHB4ENSETR |=(1<<4);

    //GPIOE MODER;
    GPIOE->MODER &=~(0b11<<20);
    GPIOE->MODER |=(0b01<<20);
    //GPIOE OTYPER;
    GPIOE->OTYPER &=~(1<<10);
    //GPIOE OSPEEDR;
    GPIOE->OSPEEDR &=~(0b00<<20);
    //GPIOE PUPDR;
    GPIOE->PUPDR &=~(0b00<<20);

     //led2初始化
    //使能led1时钟
    *RCC_MP_AHB4ENSETR &=~(1<<5);
    *RCC_MP_AHB4ENSETR |=(1<<5);

    //GPIOF MODER;
    GPIOF->MODER &=~(0b11<<20);
    GPIOF->MODER |=(0b01<<20);
    //GPIOF OTYPER;
    GPIOF->OTYPER &=~(1<<10);
    //GPIOF OSPEEDR;
    GPIOF->OSPEEDR &=~(0b00<<20);
    //GPIOF PUPDR;
    GPIOF->PUPDR &=~(0b00<<20);

    //led3初始化
    //使能led1时钟
    *RCC_MP_AHB4ENSETR &=~(1<<4);
    *RCC_MP_AHB4ENSETR |=(1<<4);

    //GPIOE MODER;
    GPIOE->MODER &=~(0b11<<16);
    GPIOE->MODER |=(0b01<<16);
    //GPIOE OTYPER;
    GPIOE->OTYPER &=~(1<<8);
    //GPIOE OSPEEDR;
    GPIOE->OSPEEDR &=~(0b00<<16);
    //GPIOE PUPDR;
    GPIOE->PUPDR &=~(0b00<<16);
}

void led_kaiguan(led_t leds,led_statue_t led_statue){
    switch (leds)
    {
    case LED1:if(led_statue==LED_on){
                    GPIOE->ODR |=(LED_on<<10);
                }else{
                    GPIOE->ODR &=~(LED_on<<10);
                }
        break;
    case LED2:if(led_statue==LED_on){
                    GPIOF->ODR |=(LED_on<<10);
                }else{
                    GPIOF->ODR &=~(LED_on<<10);
                }
        break;
    case LED3:if(led_statue==LED_on){
                    GPIOE->ODR |=(LED_on<<8);
                }else{
                    GPIOE->ODR &=~(LED_on<<8);
                }
        break;
    }

    
}

 main.c

#include "include/led.h"
void delay_ms(unsigned int ms){
        int i, j;
        for (i = 0; i < ms; i++)
            for (j = 0; j < 1800; j++)
                ;
    }
int main(){
    led_init();
    led_kaiguan(LED1,LED_on);
    while (1){
        led_kaiguan(LED1,LED_on);
        delay_ms(1000);
        led_kaiguan(LED1,LED_off);
        delay_ms(1000);

        led_kaiguan(LED2,LED_on);
        delay_ms(1000);
        led_kaiguan(LED2,LED_off);
        delay_ms(1000);

        led_kaiguan(LED3,LED_on);
        delay_ms(1000);
        led_kaiguan(LED3,LED_off);
        delay_ms(1000);
    }
    
    
}

单片机的代码一般都分文件编程,上面的方式不常用,下面是自己封装固件库的方式

按照不同功能分成不同的文件led.c  led.h  gpio.c  gpio.h  main.c

代码要有可读性,为了增加可读性,以及方便使用,采取封装库的方式,使调用者一看便知在干嘛

以hal库为例

思路:

1.gpio.h

将寄存器地址宏定义,这些在我的工程的common/include里建好了,我这里不写了
将所需gpio相关寄存器中的选项用枚举形式列举,后续有外设使用直接调用hal_gpio_init,
把枚举里的选项填进去即可
#ifndef  _GPIO_H_
#define _GPIO_H_
#include "../common/include/stm32mp1xx_gpio.h"
#include "../common/include/stm32mp1xx_rcc.h"

typedef unsigned int uint32_t;

#define GPIO_PIN_0   0x0001U
#define GPIO_PIN_1   0x0002U
#define GPIO_PIN_2   0x0004U
#define GPIO_PIN_3   0x0008U

#define GPIO_PIN_4   0x0010U
#define GPIO_PIN_5   0x0020U
#define GPIO_PIN_6   0x0040U
#define GPIO_PIN_7   0x0080U

#define GPIO_PIN_8    0x0100U
#define GPIO_PIN_9    0x0200U
#define GPIO_PIN_10   0x0400U
#define GPIO_PIN_11   0x0800U

#define GPIO_PIN_12   0x1000U
#define GPIO_PIN_13   0x2000U
#define GPIO_PIN_14   0x4000U
#define GPIO_PIN_15   0x8000U

typedef enum mode{
    Input_mode=0,
    General_purpose_output_mode=0b01,
    Alternate_function_mode=0b10,
    Analog_mode=0b11,
}mode_t;
typedef enum type{
    Output_push_pull=0,
    Output_open_drain, 
}type_t;
typedef enum speed{
    Low_speed=0,
    Medium_speed=0b01,
    High_speed=0b10,
    Very_high_speed=0b11,
}speed_t;
typedef enum pupd{
    No_pull_up_down=0,
    Pull_up=0b01,
    Pull_down=0b10,
}pupd_t;

typedef enum {
    GPIO_Reset_Pin = 0,
    GPIO_Set_Pin,
} gpio_statu_t;

typedef struct gpio{
    uint32_t pins;
    mode_t mode;
    type_t type;
    speed_t speed;
    pupd_t pupd;
}gpioinit_t;

//这里要用地址,因为要改变GPIOx中的值,用值传递的话只能在函数内部
//改变,函数结束后外部的值没变
void hal_gpio_init(gpio_t *GPIOx,gpioinit_t *InitStruct);
void hal_gpio_writePin(gpio_t *GPIOx, uint32_t pins, gpio_statu_t statu);
/*
 * 功能:gpio输入电平的状态
 * 参数:
 *      @ GPIOx :初始化哪个组的GPIO引脚 , GPIOx(A-K,Z)
 *      @ pins : 哪个gpio引脚, GPIO_PIN_0 ~ GPIO_PIN_15
 * 返回值:
 *      @ 引脚输入的状态
*/
gpio_statu_t hal_gpio_readPin(gpio_t *GPIOx, uint32_t pins);
/*
 * 功能:gpio输出电平状态翻转的状态
 * 参数:
 *      @ GPIOx :初始化哪个组的GPIO引脚 , GPIOx(A-K,Z)
 *      @ pins : 哪个gpio引脚, GPIO_PIN_0 ~ GPIO_PIN_15
 * 返回值:
 *      无
*/
void hal_gpio_tooglePin(gpio_t *GPIOx, uint32_t pins);
#endif /*_GPIO_H_*/

2.gpio.c

完成通用的hal_gpio_init函数,在这里写通用的操作寄存器的代码
#include "../include/gpio.h"

void hal_gpio_init(gpio_t *GPIOx,gpioinit_t *InitStruct){
    //初始化参数没传引脚,但是初始化需要直到引脚是多少,才能正确偏移
    //led初始化灯的时候会传,可以从参数的InitStruct里获取到
    uint32_t pinnum=0;
    for(;pinnum<15;pinnum++){
        if(InitStruct->pins & (1<<pinnum)){
            GPIOx->MODER &= ~(0x3 << (pinnum * 2));
            GPIOx->MODER |= (InitStruct->mode << (pinnum * 2));

            GPIOx->OTYPER &= ~(0x1 << pinnum );
            GPIOx->OTYPER |=(InitStruct->type <<pinnum);

            GPIOx->OSPEEDR &= ~(0x3 << (pinnum * 2));
            GPIOx->OSPEEDR |= (InitStruct->mode << (pinnum * 2));

            GPIOx->PUPDR &= ~(0x3 << (pinnum * 2));
            GPIOx->PUPDR |= (InitStruct->mode << (pinnum * 2));            
        }
    }
}



void hal_gpio_writePin(gpio_t *GPIOx, uint32_t pins, gpio_statu_t statu){
    if(statu==GPIO_Set_Pin){
        GPIOx->ODR |=pins;
    }else{
        GPIOx->ODR &=~pins;
    }
}

gpio_statu_t hal_gpio_readPin(gpio_t *GPIOx, uint32_t pins){
    gpio_statu_t ret;
    if(GPIOx->IDR & pins){//相等说明引脚输出高电平,所以返回1
        ret=GPIO_Set_Pin;
    }else{
        ret=GPIO_Reset_Pin;
    }
    return ret;
}

void hal_gpio_tooglePin(gpio_t *GPIOx, uint32_t pins){
    GPIOx->ODR ^=pins;
}

3.led.h

枚举灯的状态和灯

#ifndef  _LED_H_
#define _LED_H_
#include "../include/gpio.h"

typedef enum  ledstatue{
    led_off,
    led_on,
}ledstatue_t;

typedef enum led
{
    LED1=1,
    LED2,
    LED3,
}led_t;


void led_init();
void led_open_off(led_t ledx,ledstatue_t ledstatue);

#endif /*_LED_H_*/

4.led.c

灯的初始化函数,led_init,在里面调用hal_gpio_init
#include "../include/gpio.h"
#include "../include/led.h"

void led_init(){
    //使能GPIOE,GPIOF外设的时钟源  RCC_MP_AHB4ENSETR[5][4]
    RCC->MP_AHB4ENSETR |=(0b11<<4);

    gpioinit_t ledinitS;
    ledinitS.pins=GPIO_PIN_10 |GPIO_PIN_8;
    ledinitS.mode=General_purpose_output_mode;
    ledinitS.type=Output_push_pull;
    ledinitS.speed=Low_speed;
    ledinitS.pupd=No_pull_up_down;
    hal_gpio_init(GPIOE,&ledinitS);

    ledinitS.pins=GPIO_PIN_10;//GPIOF只有10引脚需要被初始化,其他不用动,只需改下pins即可
    hal_gpio_init(GPIOF,&ledinitS);
    
}
void led_open_off(led_t ledx,ledstatue_t ledstatue){
    switch (ledx)
    {
    case LED1:
        if (ledstatue == led_off)
            hal_gpio_writePin(GPIOE, GPIO_PIN_10, GPIO_Reset_Pin);
        else
            hal_gpio_writePin(GPIOE, GPIO_PIN_10, GPIO_Set_Pin);
        break;
    case LED2:
        if (ledstatue == led_off)
            hal_gpio_writePin(GPIOF, GPIO_PIN_10, GPIO_Reset_Pin);
        else
            hal_gpio_writePin(GPIOF, GPIO_PIN_10, GPIO_Set_Pin);
        break;
    case LED3:
        if (ledstatue == led_off)
            hal_gpio_writePin(GPIOE, GPIO_PIN_8, GPIO_Reset_Pin);
        else
            hal_gpio_writePin(GPIOE, GPIO_PIN_8, GPIO_Set_Pin);
        break;
    }
}

 main.c

#include "include/led.h"
void delay_ms(unsigned int ms){
        int i, j;
        for (i = 0; i < ms; i++)
            for (j = 0; j < 1800; j++)
                ;
    }
int main(){
    led_init();
    led_open_off(LED1,led_on);
    while (1){
        led_open_off(LED1,led_on);
        delay_ms(1000);
        led_open_off(LED1,led_off);
        delay_ms(1000);

        led_open_off(LED2,led_on);
        delay_ms(1000);
        led_open_off(LED2,led_off);
        delay_ms(1000);

        led_open_off(LED3,led_on);
        delay_ms(1000);
        led_open_off(LED3,led_off);
        delay_ms(1000);
    }
}

4、编译,下载,调试

 编译没问题

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值