我们可以来学习如何在 pmon 下操作 gpio 了, 为什么要把这个需求单独拿出来讲呢? 因为有的时候我们做了一款产品, 在特定的环境下需要让 GPIO 在上电时就是就保证是一个确定的电平, 如高电平或者低电平。 Uboot 上这些资料非常的多, 所以我们别的板子对于这个需求就没单独拿出来给大家讲, 但是龙芯用的是 pmon, pmon 上相关的资料太少了, 所以有必要单独作为一章给大家讲解。
平台:迅为i.TOP-2K1000开发板
CPU:国产龙芯处理器,双核64位系统,板载2G DDR3内存,
系统支持:busybox,buiroot,Loognix,qt
在 pmon 下控制 GPIO 有俩种方法, 这里以开发板上的 led3 给大家举例, 一种方法是在 c 语言环境建立之前来控制, 另一种方法是在 c 语言环境建立以后来控制。 我们分别来看一下这俩种方法。
1 软硬件分析
这里以开发板上的 led3 给大家举例, 我们打开开发板的底板原理图, 找到 led3 的电路, 如下图所示:
通过硬件电路图我们可以发现, 当 LS2K_GPIO0 管脚输出为高电平时, Led3 发光, 反之则不发光。 我们查阅数据手册得知, 龙芯 2K1000 共有 60 个 GPIO 引脚, 4 个为专用 GPIO, 其余 56 个与其他功能复用。
开发板上 Led3 连接的管脚为 GPIO0, 其中 GPIO0~GPIO3 为专用 GPIO 管脚, 所以不需要设置复用, 如下图所示。 注意! 如果你用的不是专用 GPIO 引脚, 则需要设置复用关系!
如果要操作一个 GPIO, 我们光知道他的复用关系还不行, 我们还要操作 GPIO 的方向寄存器以及数据寄存器,通过查阅数据手册, 我们发现 LS2K 的 GPIO 的方向寄存器的地址为 0x1fe10500, 数据寄存器的地址为0x1fe10510, 如下图所示:
方向寄存器描述:
通过上图我们可以发现, 如果为 0, 则 GPIO 的方向为输入, 反之则为输出。 0 到 63 分别对应 60 个 GPIO。数据寄存器描述:
通过上图我们可以发现, 如果为 0, 则 GPIO 输出低电平, 反之则为输高电平。 0 到 63 分别对应 60 个 GPIO。
2 通过 C 控制 GPIO
2.1 编写驱动程序
首先我们使用命令 cd Targets/LS2K/dev 进到 pmon 的 Targets/LS2K/dev 目录, 在这个目录下放的是和 LS2K相关的驱动代码, 如下图所示:
然后我们这个这个目录下使用命令 vim topeet_led.c 创建一个 c 程序, 并输入以下代码:
#include <stdio.h>
/*
- 初始化 led3 管脚
*/
void led3_init(void)
{
*(volatile int *)0xbfe10500 &= ~(0x01); //设置方向为输出
*(volatile int *)0xbfe10510 &= ~(0x01); //默认输出低电平
printf("led3 init!\n");
} /
- 点亮 led3
*/
void led3_on(void)
{
*(volatile int *)0xbfe10510 |= 0x01; //输出高电平
printf("led3 on!\n");
} /
- 熄灭 led3
*/
void led3_off(void)
{
*(volatile int *)0xbfe10510 &= ~(0x01); //输出低电平
printf("led3 off!\n");
}
为什么这里的我们操作的地址是 0xbfe10500 和 0xbfe10510 而不是我们在上一小节所说的 0x1fe10500和 0x1fe10510 呢, 因为系统上电启动后, 任何 MIPS 架构的 CPU 都是从系统的虚拟地址 0xbfc00000 启动的,其对应的物理地址为 0x1FC00000, 所以这里我们操作的是 0x1fe10500 和 0x1fe10510 对应的虚拟地址0xbfe10500 和 0xbfe10510。创建完成后如下图所示:
2.2 添加编译规则
接下来就是要把我们添加的这个 C 程序编译到 pmon 里面, 这个要如何添加呢, 如果大家看了第 9.3 小节, 我相信大家一定知道要怎么做了。我们进到 pmon 源码下的 argets/LS2K/conf 文件夹,然后使用命令 vim files.LS2K 打开配置文件, 并在配置文件的最下面添加以下代码
file Targets/LS2K/dev/topeet_led.c如下图所示:
2.3 控制 led在 9.4 小节, 我们分析了 pmon 的启动流程, 最终会在 Targets/LS2K/ls2k/tgt_machdep.c 下的 c 文件里面的 initmips 函数初始化外设, 所以我们是不是只要在这里调用我们写的 C 程序就可以了呢。
我们打开 Targets/LS2K/ls2k/tgt_machdep.c 文件, 在 initmips 函数里面添加以下代码:
led3_init();
int j=3;
while(j--)
{
led3_on();
delay(1000000);
led3_off();
delay(1000000);
}
如下图所示:
9.6.2.4 编译验证
我们将编译好的 pmon 烧写到开发板, 观察 led3 的现象, 可以发现在 pmon 启动的时候 led3 会闪烁 3次, 说明我们在 pmon 下控制 led 成功, 同时可以在控制终端看到如下打印, 如下图所示:
至此, 通过 C 控制 GPIO 完成。如大家没有成功点亮, 可以参考手册 9.7.6 章节使用 Ejtag 单步调试 pmon。
3 通过汇编控制 GPIO上一小节, 我们使用了 C 语言控制了 gpio, 这一小节我们来看一下如何使用汇编来控制 gpio 呢? 有的同学可能会有疑问了, 既然我们可以使用 C 语言来控制 gpio, 为什么我们还要使用更底层的汇编语言呢,如果我们要使用 C 语言, 是不是需要等待 C 语言环境建立起来才可以使用呢, 那他是不是就要比汇编稍微的慢一些呢, 如果我们想让他上电立马就确定 gpio 管脚的电平的状态, 使用汇编会更好些。
我们打开 Targets/LS2K/ls2k/start.S 下的 start.S 文件, 我们在 9.4.2 小节里面已经确定了他的位置, 这里就不在赘述了。
然后我们在第 487 行输入以下代码, 同样, 这里还是用 gpio0 给大家举例
/*
*设置使用 GPIO 的方向为输出
/
li v0, 0xbfe10500
ld v1, (v0)
dli a0, (1<<0) //gpio0
or v1, a0
xor v1, a0
sd v1, (v0)
/
*找到 GPIO 输出数据寄存器
/
//0x10(v0)这种写法就是给 v0 地址偏移 0x10,v0 地址是 0xbfe10500,偏移 0x10 就是 0xbfe10510,即
输出数据寄存器
ld v1, 0x10(v0)
/
*给数据寄存器写 1 输出高电平
/
or v1, a0
sd v1, 0x10(v0) //on
/
*延迟
*/
dli a2,0xffff //delay
1:
subu a2, 0x1
nop
bgtz a2, 1b
/*
*给数据寄存器写 0 输出低电平
*/
or v1, a0
xor v1, a0 //off
sd v1, 0x10(v0)
/*
*延迟
*/
dli a2,0xffff //delay
2:
subu a2, 0x1
nop
bgtz a2, 2b
如下图所示:
汇编程序的思路同 C 语言一下, 同样第一步也是先设置 gpio 的复用以及方向, 如果是专用 gpio 则不用设置复用关系。 第二步则是设置 gpio 输出的高低电平。 只不过现在我们是用汇编来完成这个操作, 如大家对汇编指令不是很清楚, 可以参考资料中的龙芯架构参考手册, 位置: LS2K1000 开发板资料\07_第三方库以其他参考资料\01_其他参考资料我们将编译好的 pmon 镜像烧写开发板, 开机上电会可以观察到开发板立刻闪烁一下, 闪烁完以后串口终端才有打印信息出现。
如大家没有成功点亮, 可以参考手册 9.7.5 章节使用 Ejtag 单步调试 pmon。
原作者:讯为电子