基于IMX6ULL的嵌入式Linux开发学习笔记——(5)GPIO标准化+调用官方IOMUXC.H函数+BEEP蜂鸣器功能实现

   在上一章讲解到,通过在头文件里设置寄存器组的基地址、寄存器结构体、重定义指针,以此来进行在函数调用中的通过结构体指针访问操作寄存。在上一章中以此方式操作了CLK和GPIO部分结构体成员,在此章中,会对更普遍化的GPIO进行结构体指针操作,并以此模块化代码对BEEP进行初始化并实现其按键功能。

GPIO通用初始化方法

在(4)中已经提到——
1.GPIO的外设寄存器组基地址为:

#define GPIO1_BASE                  (0x0209C000)
#define GPIO2_BASE                  (0x020A0000)
#define GPIO3_BASE                  (0x020A4000)
#define GPIO4_BASE                  (0x020A8000)
#define GPIO5_BASE                  (0x020AC000)

2.GPIO的重定义指针为:

#define GPIO1				((GPIO_Type *)GPIO1_BASE)
#define GPIO2				((GPIO_Type *)GPIO2_BASE)
#define GPIO3				((GPIO_Type *)GPIO3_BASE)
#define GPIO4				((GPIO_Type *)GPIO4_BASE)
#define GPIO5				((GPIO_Type *)GPIO5_BASE)

3.GPIO_Type(GPIO的寄存器布局定义)

/** GPIO - Register Layout Typedef */
typedef struct {
  __IO uint32_t DR;            /**< GPIO data register, offset: 0x0 */
  __IO uint32_t GDIR;          /**< GPIO direction register, offset: 0x4 */
  __I  uint32_t PSR;           /**< GPIO pad status register, offset: 0x8 */
  __IO uint32_t ICR1;          /**< GPIO interrupt configuration register1, offset: 0xC */
  __IO uint32_t ICR2;          /**< GPIO interrupt configuration register2, offset: 0x10 */
  __IO uint32_t IMR;           /**< GPIO interrupt mask register, offset: 0x14 */
  __IO uint32_t ISR;           /**< GPIO interrupt status register, offset: 0x18 */
  __IO uint32_t EDGE_SEL;      /**< GPIO edge select register, offset: 0x1C */
} GPIO_Type;

   以上,对于当前仅考虑输入/输出的GPIO而言,则仅需考虑GPIO方向(GDIR)和输出电平(DR)。
   为此,需要设计一个enum类型来确定输入/输出方向,再设计一个结构体类型来保存方向和输出电平。

/*enum枚举类型 设置IO输入/输出方向*/
typedef enum _gpio_pin_direction
{
    kGPIO_Digitalinput  = 0U,	//INPUT输入
    kGPIO_Digitaloutput  = 1U,	//OUTPUT输出
}gpio_pin_direction_t;

/*GPIO结构体 IO输入/输出+输出电平 */
typedef struct _gpio_pin_config
{
    gpio_pin_direction_t    direction;	//enum类型 GPIO方向
    uint8_t                 outputLogic;//输出电平
}gpio_pin_config_t;

  以上准备工作完成以后,思考:GPIO的init需要哪些参数——

  1. 首先需要GPIO的基地址(*base),即确定是哪个GPIOx(GPIO1~GPIO5);
  2. 然后确定是哪个管脚(pin);
  3. 最后通过config_t结构体确定GPIO输入输出方向(gpio_pin_config_t->direction),以及如果是输出,则输出电平(gpio_pin_config_t->outputLogic);
/*通用gpio初始化:设置管脚+输入输出方向+输出电平*/
void gpio_init(GPIO_Type *base,int pin,gpio_pin_config_t *config)
{   
    if(config->direction == kGPIO_Digitalinput) /*GPIO为输入模式*/
    {
        base->GDIR &= ~(1<<pin);	//GDIR清零,输入
    }
    else  /*GPIO为输出模式*/
    {
        base->GDIR |= (1<<pin);		//GDIR置1,输出
        /*设置默认输出电平*/
        gpio_pinwrite(base,pin,config->outputLogic);
    }
}

与此相对应的,有写GPIO和读取GPIO的位移函数。

/*写入GPIO高/低电平*/
void gpio_pinwrite(GPIO_Type *base,int pin,int value)
{
    if(value == 0)    /*写入0*/
    {
        base->DR &= ~(1<<pin);
    }
    else              /*写入1*/
    {
        base->DR |= (1<<pin);
    }
}
/*读取GPIO高/低电平*/
int gpio_pinread(GPIO_Type *base,int pin)
{
     //将读取的dr右移pin位,移动到bit0上,只需要bit0的值
    return (((base->DR) >> pin )& 0x1);
}

  以上,实现了GPIO的通用初始化模板、写入高低电平、读取GPIO电平这三个基本的操作函数。了解其基本原理即可,工作中还是直接调用以下两个函数更为常用。
IOMUXC_SetPinMux();
IOMUXC_SetPinConfig();
  所以其实自己写的目的只是为了更熟悉底层原理,知道自己要干嘛:找GPIO+管脚+方向;其实工作中需要更多的配置。
  如下为NXP的官方SDK提供的Mux和Config;但这也不影响我们对于自己写初始化的成就感的磨灭。熟悉底层原理后再用好东西,不亏。至少以后自己看着这个自己写的底层后,也能够有更通透的感觉。

static inline void IOMUXC_SetPinMux(uint32_t muxRegister,
                                    uint32_t muxMode,
                                    uint32_t inputRegister,
                                    uint32_t inputDaisy,
                                    uint32_t configRegister,
                                    uint32_t inputOnfield)
static inline void IOMUXC_SetPinConfig(uint32_t muxRegister,
                                       uint32_t muxMode,
                                       uint32_t inputRegister,
                                       uint32_t inputDaisy,
                                       uint32_t configRegister,
                                       uint32_t configValue)

BEEP初始化及其使用

  查询datasheet以及.sch原理图得知。
在这里插入图片描述
  原理图中通过一个 PNP 型的 8550三极管来驱动蜂鸣器,通过 SNVS_TAMPER1 这个 IO来控制三极管 Q1 的导通。

  • 当 SNVS_TAMPER1 输出低电平的时候 Q1 导通,相当于蜂鸣器的正极连接到 DCDC_3V3,蜂鸣器形成一个通路,因此蜂鸣器会鸣叫。
  • 当 SNVS_TAMPER1输出高电平的时候 Q1 不导通,那么蜂鸣器就没有形成一个通路,因此蜂鸣器也就不会鸣叫。
  • 简而言之,低电平-导通-鸣叫,高电平-断开-不鸣叫。
    在这里插入图片描述
    BEEP是和SNVS_TAMPER1复用管脚。
    在这里插入图片描述
    在fsl_iomuxc.h中可以查询到,SNVS_TAMPER1是和GPIO5_IO01进行管脚复用,故设置GPIO5_IO01输出低电平,则蜂鸣器响动;输出高电平则无反应。

根据上文部分的GPIO的通用初始化函数进行如下设置:

/*BEEP_init*/
void beep_init(void)
{
    /*gpio复用*/
    IOMUXC_SetPinMux(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01,0);
    /*gpio电气属性设置*/
    IOMUXC_SetPinConfig(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01,0X10B0);

    /*gpio初始化*/
    GPIO5->GDIR |=(1<<1); /*bit1设置为1,设置为输出*/
    GPIO5->DR   &=~(1<<1);/*默认关闭*/
}
void beep_switch(int status)
{
    if(status == OFF)
    {
    	/*蜂鸣器关 则输出高电平 DR=1*/
        GPIO5->DR |= (1<<1);
    }
    else if(status == ON)
    {
        /*蜂鸣器开 则输出低电平 DR=0*/
        GPIO5->DR &= ~(1<<1);
    }
}

最后主函数里简单调用即可。

int main(void)
{
	clk_enable();		/* 使能所有的时钟 			*/
	led_init();			/* 初始化led 			*/
	beep_init();

	while(1)			/* 死循环 				*/
	{	
		led_on();		/* 关闭LED 			*/
		beep_switch(ON);
		delay(1500);		/* 延时1500ms 			*/

		led_off();		/* 打开LED 			*/
		beep_switch(OFF);
		delay(1500);		/* 延时1500ms 			*/
	}

	return 0;

并且此时start.s里面写得很简单,就是进入SVC模式,设置栈顶指针就直接从汇编跳转到C语言。之后进入了中断板块,那才是真的学到离谱,不管怎样,记录,加油!23.1.16

.global _start  		/* 全局标号 */
/*
 * 描述:	_start函数,程序从此函数开始执行,此函数主要功能是设置C
 *		 运行环境。
 */
_start:

	/* 进入SVC模式 */
	mrs r0, cpsr
	bic r0, r0, #0x1f 	/* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 	*/
	orr r0, r0, #0x13 	/* r0或上0x13,表示使用SVC模式					*/
	msr cpsr, r0		/* 将r0 的数据写入到cpsr_c中 					*/

	/*设置SP指针*/
	ldr sp,=0X80200000	/* 设置栈指针			 */
	b main				/* 跳转到main函数 		 */
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

潜心修行嵌入式Linux的鼠鼠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值