Linux学习笔记4-GPIO(2)

书接上回,先回顾下STM32时的GPIO初始化过程,随便找个之前写的工程代码gpio.c文件

复制代码
void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOE_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pins : PEPin PEPin */
  GPIO_InitStruct.Pin = Key1_Pin|Key0_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = LED1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(LED1_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = Key_up_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  HAL_GPIO_Init(Key_up_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = LED0_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(LED0_GPIO_Port, &GPIO_InitStruct);

}

可以看到,单片机GPIO的初始化分为四个步骤

  1. 使能指定GPIO的时钟
  2. 初始化GPIO,比如输入还是输出,上下拉,速度
  3. 设置复用功能(上面这个例子好像没有)
  4. 如果是输出,要初始化输出高电平还是低电平,即使用HAL_GPIO_WritePin()函数

上一章其实把复用、速度设置功能弄清楚了,接下去要看时钟、设置输入输出这几个怎么做。

通过阅读手册,当IO用作GPIO时需要设置的寄存器一共有8个DR, GDIR, PSR, ICR1, ICR2, EDGE_SEL, IMRISR

I.MX6UL一共有5组GPIO,GPIO1-GPIO5,每组GPIO都有这8个寄存器。m每组GPIO最大有32个口,所以这几个寄存器基本上都是32位,每一位对应一个GPIO口,具体来看:

  • GDIR寄存器:用来设置某个GPIO口是输入还是输出,输入则设置相应的位为0,输出的话则设置为1 。比如要设置GPIO1_IO00为输入,则GPIO1.GDIR = 0,设置GPIO1_IO00输入,GPIO1_IO01为输出,则GPIO1.GDIR = 2(即0x0010)
  • DR寄存器:设置相应的IO口输出的高低电平。1为高电平,0为低电平。例如要设置GPIO1_IO01为低电平,IO00为高电平,则GPIO1.DR = 0x0001,当然GDIR寄存器要设置清楚。
  • PSR寄存器,也是32位,读取相应的位可获取该端口的高低电平值
  • ICR1和2,分别用于IO0-IO15(ICR1)和IO16-IO31(ICR2)配置中断触发方式,每个口用两位来配置:00-低电平触发;01-高电平触发;10-上升沿触发;11-下降沿触发。比如要设置GPIO1_IO15为上升沿触发,那么GPIO1.ICR1 = 2<<30(不是左移15位哦)。GPIO中断我后面会重点学习,还会打交道
  • IMR和ISR是成对的,IMR是写,即控制GPIO中断是否使能,而ISR是读,某个GPIO中断使能时则这个寄存器相应的位是1
  • 最后的EDGE_SEL寄存器用来设置边沿中断,它可以覆盖ICR1和2的设置。如果相应的位被置1,则对应的GPIO是上升沿和下降沿双触发,不用去看ICR1和2的设置。

以上就相当于单片机的GPIO属性设置,接下来看时钟。I.MX6U和STM32是类似的,每个外设的时钟都可以独立使能或禁止,这样可以关掉无关外设的时钟,省电 。需要使能GPIO的时钟时,需要设置CCM_CCGR0~CCM_CCGR6这7个寄存器中的一个,在手册里找到GPIO对应的寄存器。例如CCM_CCGR2寄存器的27-26位,对应的是GPIO3的时钟,如果要使能,将这两个位置1,可以使用CCM_CCGR2 = 3 << 26这句。(不需要解释了吧?)

好了,基本上GPIO的操作就是这些了,STM32的那4个步骤也完成了。我们来看看Linux编程实际是怎么做的。

首先会在头文件中指定这些寄存器的地址,这些地址设置可以在所有项目中使用

复制代码
...
#define CCM_CCGR2 *((volatile unsigned int *)0X020C4070)  //时钟寄存器地址,一共有7个
...
#define SW_MUX_GPIO1_IO03 *((volatile unsigned int *)0X020E0068)  //IO复用寄存器地址
#define SW_PAD_GPIO1_IO03 *((volatile unsigned int *)0X020E02F4)  //IO属性寄存器地址

#define GPIO1_DR *((volatile unsigned int *)0X0209C000)  //GPIO1相关寄存器设置的地址,一共有8个
...

接着根据需要来设置这些寄存器的值,例如

复制代码
 CCM_CCGR0 = 0xFFFFFFFF; //使能GR0管理的所有外设时钟
 SW_MUX_GPIO1_IO03 = 0x5; //把这个IO口复用为GPIO
 SW_PAD_GPIO1_IO03 = 0x10B0; //配置这个口的IO属性
 GPIO1_GDIR = 0x0000008;  //将GPIO1_IO03设置为输出
 GPIO1_DR = 0x0; //GPIO1_IO03设置为低电平输出

作为总结,我们可以看到I.MX6UL的GPIO使用远远比STM32不便,首先是没有更方便的库函数,需要去操作和了解寄存器,其次是IO口的复用太多,要深入了解每个复用,没有图形化的界面(怀念CubeMX)来简单操作。没办法,只能等后期厂商来做完善吧(但估计也没有)。

接下去会进入到GPIO中断的学习

(未完待续)

相关推荐
FserSuN3 分钟前
Machine Learning Specialization - Week 1, 9-20学习总结
人工智能·学习·机器学习
AOwhisky6 分钟前
Ceph系列第三期:Ceph 集群核心配置与管理
linux·运维·数据库·笔记·ceph
天疆说10 分钟前
在 Ubuntu 上安装 NASA GMAT R2026a 轨道设计软件
linux·运维·ubuntu
OBiO201315 分钟前
肺部靶向 AAV 怎么选?如何解决靶向不精准、转导效率低的递送难题?
学习
铅笔小新z19 分钟前
【Linux】线程同步与互斥
linux·服务器
我命由我1234525 分钟前
UGC、PGC、PUGC 极简理解
经验分享·笔记·学习·职场和发展·求职招聘·职场发展·学习方法
小何code26 分钟前
C语言【初阶】第1节,初识C语言
c语言·开发语言
七老板的blog29 分钟前
【Agent智能体】 任务规划工作流
python·学习·ai·开源
莫陌尛.35 分钟前
Fuzzy C-Mean Clustering (FCM)
c语言·开发语言
海鸥-w39 分钟前
前端学习python第二天手敲笔记整理
前端·python·学习