【龙芯1c库】封装gpio接口和使用示例
来源:互联网 发布:9988bi新域名 编辑:程序博客网 时间:2024/06/16 13:29
龙芯1c库是把龙芯1c的常用外设的常用功能封装为一个库,类似于STM32库。git地址:http://git.oschina.net/caogos/OpenLoongsonLib1c
本文首先以一个例子介绍“龙芯1c库”中gpio接口是怎样使用的,然后再详细讲解是怎样封装这些接口的。
龙芯1c库中gpio接口使用示例
gpio接口简介
龙芯1c库提供3个接口,分别是gpio_init(),gpio_set()和gpio_get()。
使用步骤为,先使用gpio_init()初始化对应的gpio,然后使用gpio_set()在gpio上输出高低电平,或者使用gpio_get()读取gpio上的电平值。
测试思路
使用led测试gpio输出,选择了GPIO6,GPIO32,GPIO74三个引脚来测试。
用按键测试gpio输入,采用不断读取gpio的方式,一旦发现按键按下,则指示灯点亮,否则指示灯熄灭。按键所在引脚为GPIO85(智龙v2.0和v2.1上的按键S3),指示灯为GPIO32。
在智龙首发版、v2.0和v2.1上gpio32都接有led,并且都可控。
代码清单
app\main.c
#include "../lib/gpio.h"#include "../lib/clock.h"#include "led.h"/* * 测试库中gpio作为输出时的相关接口 * led闪烁10次 */void gpio_test_output(void){ int i; unsigned int gpio = 32; // 智龙首发版、v2.0和v2.1都有这个led// unsigned int gpio = 6;// unsigned int gpio = 74; // 初始化 gpio_init(gpio, gpio_mode_output); // 输出10个矩形波,如果gpio50上有led,则可以看见led闪烁10次 for (i=0; i<10; i++) { gpio_set(gpio, gpio_level_low); delay(); gpio_set(gpio, gpio_level_high); delay(); } return ;}/* * 测试库中gpio作为输入时的相关接口 * 按键按下时,指示灯点亮,否则,熄灭 */void gpio_test_input(void){ unsigned int key_gpio = 85; // GPIO85/I2C_SDA0,智龙v2.1上的按键S3 unsigned int led_gpio = 32; // 用于当按键被按下时,此led点亮,否则熄灭 // 初始化 gpio_init(key_gpio, gpio_mode_input); led_init(led_gpio); while (1) { if (gpio_level_low != gpio_get(key_gpio)) continue; // 按键没有按下 // 延时(软件消抖)后再次确认按键是否按下 delay(); if (gpio_level_low != gpio_get(key_gpio)) continue; // 按键没有按下 // 点亮指示灯 led_on(led_gpio); // 等待释放按键 while (gpio_level_high != gpio_get(key_gpio)) ; delay(); // 熄灭指示灯 led_off(led_gpio); }}int main(void){ /* * 测试库中gpio作为输出时的相关接口 * led闪烁10次 */ gpio_test_output(); /* * 测试库中gpio作为输入时的相关接口 * 按键按下时,指示灯点亮,否则,熄灭 */ gpio_test_input();return(0);}
更完整的代码清单,请查看http://git.oschina.net/caogos/OpenLoongsonLib1c的提交记录
龙芯1c的GPIO简介
gpio命名规则
龙芯1c的gpio命名与其它单片机有点不一样。龙芯1c的gpio有gpio0、gpio1、gpio2………gpio103、gpio104,目前QFP176封装的编号最大达到了gpio104。其它单片机都是把gpio分为多个端口,每个端口有n个io,这点上龙芯1c是不是和其它单片机有点不一样啊。更多的具体信息请参考《龙芯1c300处理器数据手册 v1.3》如下
gpio相关寄存器
再来看看《龙芯1c300处理器用户手册v1.4》中关于gpio寄存器相关的描述
截图最下面的文字说了,假设要在gpio16上输出1,需要配置 GPIO_CFG[16]=1, GPIO_EN[16]=0, GPIO_OUT[16]=1。
同理可得把gpio16作为输入,并读取io上的值,那么需要配置GPIO_CFG[16]=1, GPIO_EN[16]=1,然后读取GPIO_IN[16]的值就知道了io上是高电平,还是低电平。
寄存器GPIO_CFG0的作用是配置某个io用作普通GPIO,还是其它功能。
寄存器GPIO_EN0的作用是配置某个gpio作为输入,还是输出。(个人认为)这个寄存器名字取得不好,en通常用于表示使能(enable)。
寄存器GPIO_IN0的作用是,当gpio作为输入时,读取寄存器GPIO_IN0中的值就得到了对应GPIO引脚上的高低电平值。
寄存器GPIO_OUT0的作用是,当gpio作为输出时,向寄存器GPIO_OUT0中写入值,那么对应的GPIO上就呈现出相应的高低电平。
注意上面截图中标题5.2.8提示GPIO_CFG0, GPIO_EN0, GPIO_IN0, GPIO_OUT0为一组寄存器,控制GPIO0到GPIO31共32个io引脚。后面还有几组类似的寄存器如下
这组寄存器对应GPIO32到GPIO63
这组寄存器对应GPIO64到GPIO95
这组寄存器对应GPIO96到GPIO127
封装代码库
代码要点
经过上面分析,其实还是蛮有规律的。封装代码库时我就把一组寄存器当作“一个端口的寄存器”,寄存器中每一位当作“一个端口中的一个io”。
“端口号”为0,1,2,3。端口号 = 龙芯1c的GPIO编号 / 32。
"一个端口中引脚的序号"从0到31,序号 = 龙芯1c的gpio编号 % 32。
转换为代码就是
#define GPIO_GET_PORT(gpio) ((gpio) / 32)#define GPIO_GET_PIN(gpio) ((gpio) % 32)再来回顾一下前面介绍的gpio配置过程
假设要在gpio16上输出1,需要配置 GPIO_CFG[16]=1, GPIO_EN[16]=0, GPIO_OUT[16]=1。
同理可得把gpio16作为输入,并读取io上的值,那么需要配置GPIO_CFG[16]=1, GPIO_EN[16]=1,然后读取GPIO_IN[16]的值就知道了io上是高电平,还是低电平。
将以上步骤用伪代码来表示就是gpio_init(GPIOn, 输入或输出){ 通过设置寄存器GPIO_CFGx,将引脚GPIOn配置为普通gpio 通过设置寄存器GPIO_ENx,将引脚GPIOn配置为输入或输出}gpio_set(GPIOn, 0或1){ 通过设置寄存器GPIO_OUTx,在引脚GPIOn上输出0或1}gpio_get(GPIOn){ 通过读取寄存器GPIO_INx,获取引脚GPIOn上的电平值}
具体代码见下面的代码清单
完整的代码清单
lib\gpio.h
#ifndef __OPENLOONGSON_GPIO_H#define __OPENLOONGSON_GPIO_H// gpio的工作模式--输入、输出typedef enum{ gpio_mode_output = 0, // 输出 gpio_mode_input = 1 // 输入}gpio_mode_t;// gpio高低电平值typedef enum{ gpio_level_low = 0, // 低电平 gpio_level_high = 1 // 高电平}gpio_level_t;/* * gpio初始化 * @gpio gpio引脚,取值范围[0, 127] * @mode gpio的工作模式(输入、输出) * * 例: 将gpio50初始化为输出 * gpio_init(50, gpio_mode_output); */void gpio_init(unsigned int gpio, gpio_mode_t mode);/* * 在指定gpio输出高电平或低电平 * @gpio gpio引脚,取值范围[0, 127] * @level 电平值 * * 例: 在gpio50上输出低电平 * gpio_set(50, gpio_level_low); */void gpio_set(unsigned int gpio, gpio_level_t level);/* * 读取指定gpio引脚的值 * @gpio gpio引脚,取值范围[0,127] * * 例: 读取gpio50引脚上的值 * gpio_level_t level; * level = gpio_get(50); */gpio_level_t gpio_get(unsigned int gpio);#endif
lib\gpio.c
// 封装gpio接口#include "gpio.h"// 寄存器地址#define LS1C_GPIO_CFG0 (0xbfd010c0)#define LS1C_GPIO_EN0 (0xbfd010d0)#define LS1C_GPIO_IN0 (0xbfd010e0)#define LS1C_GPIO_OUT0 (0xbfd010f0)#define LS1C_GPIO_CFG1 (0xbfd010c4)#define LS1C_GPIO_EN1 (0xbfd010d4)#define LS1C_GPIO_IN1 (0xbfd010e4)#define LS1C_GPIO_OUT1 (0xbfd010f4)#define LS1C_GPIO_CFG2 (0xbfd010c8)#define LS1C_GPIO_EN2 (0xbfd010d8)#define LS1C_GPIO_IN2 (0xbfd010e8)#define LS1C_GPIO_OUT2 (0xbfd010f8)#define LS1C_GPIO_CFG3 (0xbfd010cc)#define LS1C_GPIO_EN3 (0xbfd010dc)#define LS1C_GPIO_IN3 (0xbfd010ec)#define LS1C_GPIO_OUT3 (0xbfd010fc)// 龙芯1c的gpio是按照0,1,2,3,4...这样的顺序编号的,// 但在操作寄存器的时候,又是按照每32个一组来分的// 这里利用这个特性,将每组的32个gpio叫做一个"port",每个gpio在每组中的索引叫"pin"// port = gpio / 32// pin = gpio % 32// 例如GPIO50,port=1,pin=18#define GPIO_GET_PORT(gpio) ((gpio) / 32)#define GPIO_GET_PIN(gpio) ((gpio) % 32)/* * gpio初始化 * @gpio gpio引脚,取值范围[0, 127] * @mode gpio的工作模式(输入、输出) * * 例: 将gpio50初始化为输出 * gpio_init(50, gpio_mode_output); */void gpio_init(unsigned int gpio, gpio_mode_t mode){ volatile unsigned int *gpio_cfgx; // GPIO_CFGx寄存器 volatile unsigned int *gpio_enx; // GPIO_ENx寄存器 unsigned int port = GPIO_GET_PORT(gpio); unsigned int pin = GPIO_GET_PIN(gpio); unsigned int temp; unsigned int mask; switch (port) { case 0: gpio_cfgx = (volatile unsigned int *)LS1C_GPIO_CFG0; gpio_enx = (volatile unsigned int *)LS1C_GPIO_EN0; break; case 1: gpio_cfgx = (volatile unsigned int *)LS1C_GPIO_CFG1; gpio_enx = (volatile unsigned int *)LS1C_GPIO_EN1; break; case 2: gpio_cfgx = (volatile unsigned int *)LS1C_GPIO_CFG2; gpio_enx = (volatile unsigned int *)LS1C_GPIO_EN2; break; case 3: gpio_cfgx = (volatile unsigned int *)LS1C_GPIO_CFG3; gpio_enx = (volatile unsigned int *)LS1C_GPIO_EN3; break; default: // 正确的程序不应该走到这里,直接返回 return ; } // 将pin设为普通GPIO mask = 1 << pin; temp = *gpio_cfgx; temp |= mask; // 将指定位置1 *gpio_cfgx = temp; // 设置gpio工作模式(输入、输出) temp = *gpio_enx; if (gpio_mode_output == mode) // 输出 { temp &= ~mask; // 将指定位清零 } else // 输入 { temp |= mask; // 将指定位置1 } *gpio_enx = temp; return ;}/* * 在指定gpio输出高电平或低电平 * @gpio gpio引脚,取值范围[0, 127] * @level 电平值 * * 例: 在gpio50上输出低电平 * gpio_set(50, gpio_level_low); */void gpio_set(unsigned int gpio, gpio_level_t level){ volatile unsigned int *gpio_outx; // GPIO_OUTx寄存器 unsigned int port = GPIO_GET_PORT(gpio); unsigned int pin = GPIO_GET_PIN(gpio); unsigned int temp; unsigned int mask; switch (port) { case 0: gpio_outx = (volatile unsigned int *)LS1C_GPIO_OUT0; break; case 1: gpio_outx = (volatile unsigned int *)LS1C_GPIO_OUT1; break; case 2: gpio_outx = (volatile unsigned int *)LS1C_GPIO_OUT2; break; case 3: gpio_outx = (volatile unsigned int *)LS1C_GPIO_OUT3; break; default: // 正确的程序不应该走到这里,直接返回 return ; } mask = 1 << pin; temp = *gpio_outx; if (gpio_level_low == level) { temp &= ~mask; // 输出低电平 } else { temp |= mask; // 输出高电平 } *gpio_outx = temp; return ;}/* * 读取指定gpio引脚的值 * @gpio gpio引脚,取值范围[0,127] * * 例: 读取gpio50引脚上的值 * gpio_level_t level; * level = gpio_get(50); */gpio_level_t gpio_get(unsigned int gpio){ volatile unsigned int *gpio_inx; // GPIO_INx寄存器 unsigned int port = GPIO_GET_PORT(gpio); unsigned int pin = GPIO_GET_PIN(gpio); unsigned int temp; gpio_level_t level; switch (port) { case 0: gpio_inx = (volatile unsigned int *)LS1C_GPIO_IN0; break; case 1: gpio_inx = (volatile unsigned int *)LS1C_GPIO_IN1; break; case 2: gpio_inx = (volatile unsigned int *)LS1C_GPIO_IN2; break; case 3: gpio_inx = (volatile unsigned int *)LS1C_GPIO_IN3; break; default: // 正常的流程不应该走到这里,直接返回 return ; } temp = *gpio_inx; temp = (temp >> pin) & 1; if (temp) { level = gpio_level_high; } else { level = gpio_level_low; } return level;}
- 【龙芯1c库】封装gpio接口和使用示例
- 【龙芯1c库】封装时钟接口和使用示例
- 【龙芯1c库】封装硬件pwm接口和使用示例
- 【龙芯1c库】封装引脚复用接口和使用示例
- 【龙芯1c库】封装软件延时接口和使用示例
- 【龙芯1c库】封装模拟I2C接口和使用示例
- 【龙芯1c库】封装硬件定时器接口和使用示例
- 【龙芯1c库】封装硬件I2C接口和使用示例
- 【龙芯1c库】封装硬件SPI接口和使用示例
- Linux GPIO 使用示例
- 在龙芯1c上使用RT-Thread统一标准的gpio接口
- Linux GPIO - gpio.c(GPIO各个接口的实现)
- HI35XX GPIO使用及示例
- 1、GPIO接口--s3c2440
- C/S架构移动网络Socket API接口 Socket网络库 xNet框架介绍(C++封装) Demo演示示例
- 接口和父类的使用示例
- DSP 2812: 使用C++封装GPIO
- GPIO示例
- 【2017.04.19】腾讯二面面经
- Java Out of Memory 分析
- tab切换
- swoole 安装方法
- 计算机中的同步和异步
- 【龙芯1c库】封装gpio接口和使用示例
- c++实验4
- ryzom-contribution /code/ryzom/server/shard_unifier_service_default.cfg
- Android Touch事件分发机制详解之由点击引发的战争
- Chrome表单如何去掉默认的淡黄色背景及如何关闭浏览器自带填充表单功能?
- python 判断网络是否畅通
- Android学习笔记(二十三):GridVeiw的高度设置为屏幕高度的一半
- 向量叉乘
- Linux 常用基本指令