LPC824-GPIO端口应用

发布时间:2023-05-25  

通过前面的分析,我们知道在LPC824中,对GPIO端口的操作一共涉及到68个寄存器,那究竟该使用哪些寄存器,特别是对于具有相同功能的寄存器,应该如何选择,下面就来进行具体讨论。

虽然一共有68个寄存器,但其中的端口字节引脚寄存器B和端口字引脚寄存器W,由于每个对应一根引脚,所以就分别占用了29个寄存器,一共占用了58个,因此可以把B寄存器和W寄存器算成两个(两类),这样的话一共就只有12个寄存器了。在LPC824中,要实现对GPIO的同一个控制,操作的寄存器可有多种选择。例如:要控制某个引脚输出高电平,可以选择的寄存器就有PIN0、W、B、SET0及MPIN0等四个。那究竟应该如何选择,在LPC824的官方手册中给出了如下建议: 

(1)对于复位或重新初始化后的初始设置,写PORT寄存器(即PIN0寄存器)。
(2)如需更改某个引脚的状态,写字节引脚或字引脚寄存器。
(3)如需一次性更改多个引脚的状态,写SET和(或)CLR寄存器。
(4)如需在严格控制的环境(如软件状态机)中更改多个引脚的状态,可考虑使用NOT寄存器,这比SET和CLR需要的写操作更少。
(5)如需读取某个引脚的状态,读字节引脚或字引脚寄存器。
(6)如需根据多个引脚作出决定,读取并屏蔽PORT寄存器(即MPIN0寄存器)。

一般来说,当要控制的GPIO引脚是“一次性”操作的话,就操作PIN0寄存器,因为它会一次性把29位电平全部输出到对应的端口引脚上,或一次性把29根端口引脚上的电平全部读入到寄存器中,效率较高,比较适合芯片初始化时的引脚电平赋值、全端口电平取反等操作。而对于只控制单一的某一根引脚状态(读或写)时,可操作W或B寄存器,因为它只更改端口的其中一位的值而不会影响到其他位,不必像传统使用“与或”操作的方式那样麻烦。对于一次要控制多根引脚状态时,可根据情况来操作SET0和CLR0寄存器,其中SET0寄存器为指定的引脚输出高电平,而其他引脚的电平保持不变,CLR0则相反,为指定的引脚输出低电平,其他引脚的电平保持不变。但使用SET0和CLR0寄存器时要特别注意,它们中值为0的位所对应的引脚上的电平是保持原有不变,而不是输出低电平。当要对某些引脚进行电平取反时,可操作NOT0寄存器,值为1的位所对应的引脚电平取反,为0的保持原有电平不变。前面的操作均不受MASK0寄存器的影响,当要对端口的某些引脚确定其操作状态时,即只能针对某些引脚进行控制时,可操作MPIN0寄存器,而对其中能操作的引脚则通过MASK0寄存器来进行设置,MASK0中值为1的位所对应的引脚将被“固定”住其原来的状态不变,值为0的位所对应的引脚才能通过写MPIN0寄存器来更改。

同样的道理,对于方向寄存器DIR0、DIRSET0、DIRCLR0及DIRNOT0的使用也可参考上述原则进行。当要“一次性”确定所有引脚的方向时,就操作DIR0寄存器,原理就不再赘述了。而对于要一次更改多个(或单个)引脚的方向时,可根据情况来操作DIRSET0和DIRCLR0寄存器,其中DIRSET0寄存器为指定的引脚更改为输出方向,而其他引脚的方向保持不变,DIRCLR0则相反,为指定的引脚更改为输入方向,其他引脚的方向保持不变。同样,在使用DIRSET0和DIRCLR0寄存器时要注意,它们中值为0的位所对应的引脚方向是保持原来的不变,而不是变为输入。当只针对某些引脚的方向进行取反时,可操作DIRNOT0寄存器,值为1的位所对应的引脚方向取反,为0的保持原有方向不变。

通过上述分析,就可明确这12个端口寄存器的具体用法了。下面再通过修改前面第一个演示示例的例子,来看一下这12个端口寄存器可以怎样操作。为了简化,这里只罗列出需要修改的部分,其余部分照旧。下面给出的是原来的端口初始化函数。

void Port_init(void)
{
 LPC_GPIO_PORT->DIR0 = 0x1FFFFFFF;        //设置端口为输出方向
 LPC_GPIO_PORT->PIN0 = 0x10090080;        //输出相应电平交替点亮LED
}

由于是端口初始化,即“一次性”操作,所以这里访问的是DIR0和PIN0两个寄存器,用来设置引脚的输出方向和电平高低。下面是定时器中断服务函数,用来把引脚的输出电平取反。

void SysTick_Handler(void)
{
  LPC_GPIO_PORT->PIN0 = ~LPC_GPIO_PORT->PIN0;    //取反赋值
}

在原来的程序中,访问的依然是PIN0寄存器,因为这里的取反也属于对端口的“一次性”操作。然而,该语句也可以更改为下面的样子。

void SysTick_Handler(void)
{
  LPC_GPIO_PORT->NOT0 = 0x1FFFFFFF;    //端口引脚电平取反
}

由于在NOT0寄存器中,值为1的位对应的引脚电平取反,所以把PIO0~PIO28的共29个位全部设置为1,也可以实现LPC_GPIO_PORT->PIN0 = ~LPC_GPIO_PORT->PIN0的取反效果。

除此以外,还可以通过访问MPIN0和MASK0两个寄存器来实现同样的效果。两个函数更改如下。

void Port_init(void)
{
 LPC_GPIO_PORT->DIR0 = 0x1FFFFFFF;        //设置端口为输出方向
 LPC_GPIO_PORT->MPIN0 = 0x10090080;        //输出相应电平交替点亮LED
 // LPC_GPIO_PORT->MASK0 = 0x1FFFFFFF;   //屏蔽输出引脚
}
void SysTick_Handler(void)
{
  LPC_GPIO_PORT->MPIN0 = ~LPC_GPIO_PORT->MPIN0;    //取反赋值
}

按上述更改重新编译程序,会发现当注释掉对MASK0操作这一句的话,运行效果是与前面一致的。而当把注释掉的语句恢复后,则在运行时LED就不再闪烁了。原因是MASK0中值为1的位对应的引脚被屏蔽了,不会因MPIN0的赋值而改变。还可再进一步验证,把MASK0的赋值改成和MPIN0的值一样,即LPC_GPIO_PORT->MASK0 = 0x10090080,然后编译运行看看效果。可看到,LED变成了隔空闪烁而不是交替闪烁,这是为什么呢?仔细来分析一下,为了方便我们不防假设只有4个LED,而且是紧挨着的,依然是低电平点亮(即值为0时亮)。初始状态假设为“1010”,即MPIN0和MASK0的值都为“1010”。MASK0中值为1的位保持原来状态不变,即LED状态应为“灭X灭X”,其中X的状态由MPIN0的值确定。初始时X对应的值都为0(即低电平),所以初始状态的LED为“灭亮灭亮”。当MPIN0取反后值变为“0101”,X的值都由0变成了1(即高电平),所以X对应的LED状态为灭,因此加上MASK0中的屏蔽位后,LED的状态为“灭灭灭灭”,即全灭。而当MPIN0再次取反后,X的值又变为了0,所以加上MASK0中的屏蔽位后,LED的状态又变为了“灭亮灭亮”。可见,最终LED的效果就是隔空闪烁。

同理还可再做其他尝试,比如保持上述的端口初始化不变,而把中断服务函数改成下面的样子。

void SysTick_Handler(void)
{
  LPC_GPIO_PORT->PIN0 = ~LPC_GPIO_PORT->PIN0;    //取反赋值
// LPC_GPIO_PORT->NOT0 = 0x1FFFFFFF;    //端口引脚电平取反
}

编译后运行程序,会发现无论是操作PIN0取反,还是操作NOT0取反,都不影响LED的交替闪烁,这就证明了PIN0和NOT0两个寄存器确实不受MASK0寄存器的影响。

同样,为了验证使用SET0和CLR0两个寄存器的效果,还可以把中断服务函数改成下面的样子。

void SysTick_Handler(void)
{
 uint32_t temp;
 temp = LPC_GPIO_PORT->PIN0;
 LPC_GPIO_PORT->SET0 = ~temp;
 LPC_GPIO_PORT->CLR0 = temp;

可以看到,使用SET0和CLR0两个寄存器来实现同样的效果,就稍为麻烦一些。为了便于理解,我们仍然使用前面假设的4个LED。假设初始状态仍为“1010”,则LED状为“灭亮灭亮”,取反后的值为“0101”,赋值给SET0后,值为0的位对应的引脚电平保持不变,而值为1的位对应的引脚输出高电平,所以LED的状态变为了“灭灭灭灭”,即全灭。而给CLR0的赋值仍为“1010”,值为0的位对应的引脚电平保持不变,而值为1的位对应的引脚输出低电平,所以LED的状态又变为了“亮灭亮灭”,这样就实现了LED的交替闪烁。同时,为了便于操作端口,还定义了一个局部变量来存取上一次的端口值。需要强调的是,这里仅仅是为了学习而演示,此种方法在实际工程中是不建议采用的,不是因为复杂,而是因为它不可靠,细究会发现在端口上有一瞬间是输出了全1的(即LED全灭),虽然时间非常非常短,但仍然存在控制上的隐患,所以仅适用于学习。 

在上述验证中,还可以把temp = LPC_GPIO_PORT->PIN0改成temp = LPC_GPIO_PORT->MPIN0,并把端口初始化中的MASK0进行启用或禁用,逐步来验证效果,以提高对端口屏蔽的认识,具体操作在此就不赘述了。

下面来看一个键控灯的小例子,电路如下图所示。

 

要求通过按键KEY来控制LED的亮灭,即按下按键LED亮,松开按键LED灭。程序代码如下。

#include

//************************端口初始化***********************************
void Port_init(void)
{
LPC_GPIO_PORT->DIRSET0 = 0x80; //设置引脚PIO0_7为输出方向
LPC_GPIO_PORT->DIRCLR0 = 0x01; //设置引脚PIO0_1为输入方向
LPC_GPIO_PORT->SET0 = 0x80; //设置引脚PIO0_7输出高电平
}
//***************************主函数************************************
int main(void)
{
Port_init(); //调用端口初始化
while(1)
{
if(LPC_GPIO_PORT->B1)
LPC_GPIO_PORT->B7 = 1; //按键释放,LED灭
else
LPC_GPIO_PORT->B7 = 0; //按键按下,LED亮
}
}

在上述程序中,端口初始化函数并没有直接操作PIN0寄存器,而是分别操作了DIRSET0、DIRCLR0和SET0寄存器。 根据前面的电路图可知,按键和LED一共只接了两根引脚,所以只需要对这两根引脚进行操作就可以了,其他的引脚不要去改变它。DIRSET0寄存器中值为1的位对应的引脚被设置为输出方向,而值为0的位对应的引脚不变。所以执行了LPC_GPIO_PORT->DIRSET0=0x80这一句后,只有引脚PIO0_7被设置为输出方向,并不会去改变其他引脚的方向。同理,DIRCLR0寄存器中值为1的位对应的引脚被设置为输入方向,而值为0的位对应的引脚不变。所以执行了LPC_GPIO_PORT->DIRCLR0=0x01这一句后,只有引脚PIO0_1被设置为输入方向,也不会去改变其他引脚的方向。对应电路原理图,引脚PIO0_7接的是LED,为输出方向,引脚PIO0_1接的是按键,为输入方向,所以执行了端口初始化函数后,LED和按键的驱动方向也就设置好了。

接下来在主函数中,通过读取引脚PIO0_1上的电平,就可心判断按键是否被按下了,这里读取的是字节引脚寄存器B1(对应引脚PIO0_1),而没有读取PIN0寄存器。因为这里只读取一个位的值,不必全部位都读取,大大提高了操作效率。同理,在点亮(或熄灭)LED时,也是操作字节引脚寄存器B7(对应引脚PIO0_7),而没有操作PIN0寄存器,同样提高了操作效率。

把上述程序编译下载到LPC824中并执行,可看到初始时LED是不亮的,按下KEY键LED发光,释放后LED熄灭,达到了设计要求。此外,还可把操作的字节引脚寄存器B换成字引脚寄存器W,即B1换成W1,B7换成W7,重新编译执行,会发现效果不变。说明在进行位操作时,即可以访问字节引脚寄存器也可访问字引脚寄存器,效果是一样的。那究竟应该选择哪个引脚寄存器呢?一般地,如果引脚上只写(或读)0和1两个值的话,就用字节引脚寄存器B,如果写(或读)的是0和非0多个值的话,就用字引脚寄存器W。实际上W寄存器是为了兼容而设计的,一般访问B寄存器就可以了。

最后还有一个DIRNOT0寄存器,这个寄存器在值为1的位对应的引脚会进行方向的取反,即输入变输出或输出变输入。这为单引脚上需要经常改变方向的操作提供了极大的便利,比如在一些单总线的设备模块访问上,就经常需要改变方向,这时直接使用DIRNOT0寄存器就方便多了,这将在以后用到时再作讲解,现在暂时不讨论了。

最后再强调一下,在芯片复位后,默认所有的引脚均为输入方向,所以复位后B、W、PIN0、MPIN0等4个寄存器的值是由外部电路来决定的。

通过上述讨论可见,在LPC824中操作GPIO端口的寄存器有很多,这为开发提供了极大的便利,所以在进行程序设计的时候,就应该尽量避免对端口寄存器进行“与或”等操作了,这样可大大提高操作效率。


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

相关文章

    译出来的文件拷贝到开发板挂接的网络文件系统上 4)、执行insmod second_drv.ko加载驱动。 5)、./second_test测试程序,按下按键,成功打印按键值,用top命令查看应用程序......
    是一个软件组件, 封装了应用程序存取硬件设备的功能函数。有些设备具有相同的属性, 把它们归为一组标准类别, 可以定义设备类规范作为该类设备的主机驱动框架。设备类驱动程序使用相似的函数, 处理不同设备间的通信,这样使设备类驱动程序......
    是一个软件组件, 封装了应用程序存取硬件设备的功能函数。有些设备具有相同的属性, 把它们归为一组标准类别, 可以定义设备类规范作为该类设备的主机驱动框架。设备类驱动程序使用相似的函数, 处理......
    )。此修改释放了许多额外的端口引脚供我们的应用程序使用,否则这些引脚将用于实现扩展的内存总线,特别是端口 0(所有八行)、端口 2(所有八行)、端口 3.6 和 3.7。请参阅表 1。(注意:此示例应用程序中不使用端口......
    购买的“ChatGPT Plus”版本,区别在于:ChatGPT Plus的订阅用户可以在手机上享有GPT-4的系统、优先访问的功能以及更快的响应速度。OpenAI表示,为ChatGPT Plus订阅付费的用户还可以通过该应用程序使用......
    是可以成功的,但是集成到客户应用中时就无法成功。那我们接下来重点研究应用程序环境。 应用程序环境 应用程序使用了 FreeRTOS,基于 IAR 编译环境。 查看库文件的使用 确认使用......
    校验是可以成功的,但是集成到客户应用中时就无法成功。那我们接下来重点研究应用程序环境。 应用程序环境 应用程序使用了 FreeRTOS,基于 IAR 编译环境。 查看库文件的使用 确认使用......
    址模式,允许跨 64kB 边界轻松访问代码和数据。扩展堆栈也已启用,利用DS80C400的专用1024字节堆栈空间。这留下了间接内存空间可供应用程序使用,而不必担心堆栈使用会破坏其内容。应用启动后,时钟......
    是无线网卡上最基本的控制系统,主要基于MAC芯片来实现对整个网卡的控制和管理,并向下提供与物理层的接口,向上提供一个程序开发接口,为程序开发附加的移动主机应用程序提供支持。在嵌......
    供get_stats函数接收它。   (3)应用程序使用标准的socket、bind、send等操作。   本文采用第一种方法进行双向ping通测试。首先在开发板终端上输入命令ifconfig eth0......

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>