1、概述
这段代码中采用了使用mmap函数映射IO寄存器来控制IO的方法,可视为应用层上最快速的IO控制方式之一。
在第三节代码中有ADS8361芯片的时序控制代码,经过多次测试,每次采集所需的时间约为38微秒,共进行了78次IO控制操作,平均每次IO控制操作耗时约为0.5微秒。
2、原理
使用内存映射时条件和初始化:
(1.)内核中释放用到这些IO的设备树节点;
(2.)先手动添加,或在应用层通过程序,将引脚导出(寄存器不对时使用的);
(3.)使用mmap函数将GPIO寄存器映射到虚拟内存;
(4.)通过寄存器设置GPIO引脚复用,设置GPIO的输入输出等参数;
(5.)或者手动添加,在应用层通过程序,设置GPIO的输入输出等参数;
使用内存映射时的寄存器地址,就和STM32一样。但要注意偏移地址!!!
对指针偏移不理解的宝子们看👇
C语言的指针偏移_c语言指针偏移怎么写_颜一书的博客-CSDN博客
3、代码
#include <sys/mman.h>
#include<stdio.h> /* 标准输入输出定义 */
#include<stdlib.h> /* 标准函数库定义 */
#include<unistd.h> /* Unix 标准函数定义 */
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h> /* 文件控制定义 */
#include<termios.h> /* PPSIX 终端控制定义 */
#include<string.h>
#include <sys/time.h>
#include <ctype.h>
#include <linux/gpio.h>
/**
ADC_CLOCK GPIO0-B5 GRF--H:4~6=0 pinx =L- 13
ADC_DATA_A0 GPIO0-C5 GRF--H:4~6=0 pinx =H- 5
ADC_DATA_B GPIO4-D0 GRF--L:0~2=0 pinx =H- 8
ADC_BUSY GPIO0-C7 GRF--H:12~14=0 pinx =H- 7
ADC_CONVS GPIO4-C2 GRF--L:8~10=0 pinx =H- 2
M0 GPIO4-D1 GRF--L:4~6=0 pinx =H- 9
M1 GPIO4-C7 GRF--H:12~14=0 pinx =H- 7
A0 GPIO4-C3 GRF--L:12~14=0 pinx =H- 3
**/
volatile unsigned int * PMU_GRF_GPIO0;
volatile unsigned int * PMU_GRF_GPIO4;
volatile unsigned int * REGGPIO0;
volatile unsigned int * REGGPIO4;
#define PMU_GRF_GPIO0_BASEADDR 0XFDC20000
#define PMU_GRF_GPIO4_BASEADDR 0XFDC60000
#define GPIO0_REG_BASEADDR 0XFDD60000
#define GPIO4_REG_BASEADDR 0XFE770000
volatile unsigned int * PMU_GRF_GPIO0;
volatile unsigned int * PMU_GRF_GPIO4;
volatile unsigned int * REGGPIO0;
volatile unsigned int * REGGPIO4;
#define PMU_GRF_GPIO0A_IOMUX_L 0X0000U
#define PMU_GRF_GPIO0A_IOMUX_H 0X0004U
#define PMU_GRF_GPIO0B_IOMUX_L 0X0008U
#define PMU_GRF_GPIO0B_IOMUX_H 0X000CU
#define PMU_GRF_GPIO0C_IOMUX_L 0X0010U
#define PMU_GRF_GPIO0C_IOMUX_H 0X0014U
#define PMU_GRF_GPIO0D_IOMUX_L 0X0018U
#define PMU_GRF_GPIO0D_IOMUX_H 0X001CU
#define PMU_GRF_GPIO4A_IOMUX_L 0X0060U
#define PMU_GRF_GPIO4A_IOMUX_H 0X0064U
#define PMU_GRF_GPIO4B_IOMUX_L 0X0068U
#define PMU_GRF_GPIO4B_IOMUX_H 0X006CU
#define PMU_GRF_GPIO4C_IOMUX_L 0X0070U
#define PMU_GRF_GPIO4C_IOMUX_H 0X0074U
#define PMU_GRF_GPIO4D_IOMUX_L 0X0078U
#define PMU_GRF_GPIO4D_IOMUX_H 0X007CU
#define GPIO0_DATA_L 0X0000U
#define GPIO0_DATA_H 0X0004U
#define GPIO0_DIR_L 0X0008U
#define GPIO0_DIR_H 0X000CU
#define GPIO0_INT_L 0X0010U
#define GPIO0_INT_H 0X0014U
#define GPIO0_EXT_PORT 0X0070U
#define GPIO4_DATA_L 0X0000U
#define GPIO4_DATA_H 0X0004U
#define GPIO4_DIR_L 0X0008U
#define GPIO4_DIR_H 0X000CU
#define GPIO4_INT_L 0X0010U
#define GPIO4_INT_H 0X0014U
#define GPIO4_EXT_PORT 0X0070U
enum
{
ADC_CLOCK=0,
ADC_DATA_A,
ADC_DATA_B,
ADC_BUSY,
ADC_CONVST,
M0,
M1,
A0,
};
struct gpio_t
{
unsigned char gpio;
char number[10];
char direction[100];
char gpio_path[100];
int fd;
};
struct gpio_t ADC_collect[8]=
{
{ADC_CLOCK ,"13" ,"out","",0},
{ADC_DATA_A,"21" ,"in" ,"",0},
{ADC_DATA_B,"152","in" ,"",0},
{ADC_BUSY ,"23" ,"in" ,"",0},
{ADC_CONVST,"146","out","",0},
{M0,"153","out","",0},
{M1,"151","out","",0},
{A0,"147","out","",0},
};
/**
* 函数名称 __raw_writel(unsigned int data, volatile unsigned int *reg)
* 函数功能 往指定地址写数据
* 输入参数 unsigned int data :写入的数据
volatile unsigned int *reg:要写入数据的地址
* 函数返回值 无
*/
void __raw_writel(unsigned int data, volatile unsigned int *reg)
{
*reg=data;
}
/**
* 函数名称 __raw_readl( volatile unsigned int *reg)
* 函数功能 从指定地址读数据
* 输入参数 volatile unsigned int *reg:要读取数据的地址
* 函数返回值 读取的数据
*/
unsigned int __raw_readl( volatile unsigned int *reg)
{
unsigned int data=0;
data=*reg;
return data;
}
/**
函數名: ADC_GPIO_Init
函數功能: 初始化ADC模塊引脚的GPIO
輸入: 无
輸出: 无
**/
void ADC_GPIO_Init(void)
{
unsigned int temp=0U;
/*引脚用途模式*/
/*ADC_CLOCK*/
temp=__raw_readl(PMU_GRF_GPIO0+PMU_GRF_GPIO0B_IOMUX_H);
temp = temp | 0xffff0000;
__raw_writel(temp,PMU_GRF_GPIO0+PMU_GRF_GPIO0B_IOMUX_H);
temp = temp & (~(0x0000000FU<<4U));
__raw_writel(temp,PMU_GRF_GPIO0+PMU_GRF_GPIO0B_IOMUX_H);
/*ADC_DATA_A0*/
temp=__raw_readl(PMU_GRF_GPIO0+PMU_GRF_GPIO0C_IOMUX_H);
temp = temp | 0xffff0000;
__raw_writel(temp,PMU_GRF_GPIO0+PMU_GRF_GPIO0C_IOMUX_H);
temp = temp & (~(0x0000000fU<<4U));
__raw_writel(temp,PMU_GRF_GPIO0+PMU_GRF_GPIO0C_IOMUX_H);
/*ADC_DATA_B*/
temp=__raw_readl(PMU_GRF_GPIO4+PMU_GRF_GPIO4D_IOMUX_L);
temp = temp | 0xffff0000;
__raw_writel(temp,PMU_GRF_GPIO4+PMU_GRF_GPIO4D_IOMUX_L);
temp = temp & (~(0x0000000fU<<0U));
__raw_writel(temp,PMU_GRF_GPIO4+PMU_GRF_GPIO4D_IOMUX_L);
/*ADC_BUSY*/
temp=__raw_readl(PMU_GRF_GPIO0+PMU_GRF_GPIO0C_IOMUX_H);
temp = temp | 0xffff0000;
__raw_writel(temp,PMU_GRF_GPIO0+PMU_GRF_GPIO0C_IOMUX_H);
temp = temp & (~(0x0000000fU<<12));
__raw_writel(temp,PMU_GRF_GPIO0+PMU_GRF_GPIO0C_IOMUX_H);
/*ADC_CONVS*/
temp=__raw_readl(PMU_GRF_GPIO4+PMU_GRF_GPIO4C_IOMUX_L);
temp = temp | 0xffff0000;
__raw_writel(temp,PMU_GRF_GPIO4+PMU_GRF_GPIO4C_IOMUX_L);
temp = temp & (~(0x0000000fU<<8));
__raw_writel(temp,PMU_GRF_GPIO4+PMU_GRF_GPIO4C_IOMUX_L);
/*M0*/
temp=__raw_readl(PMU_GRF_GPIO4+PMU_GRF_GPIO4D_IOMUX_L);
temp = temp | 0xffff0000;
__raw_writel(temp,PMU_GRF_GPIO4+PMU_GRF_GPIO4D_IOMUX_L);
temp = temp & (~(0x0000000fU<<4U));
__raw_writel(temp,PMU_GRF_GPIO4+PMU_GRF_GPIO4D_IOMUX_L);
/*M1*/
temp=__raw_readl(PMU_GRF_GPIO4+PMU_GRF_GPIO4C_IOMUX_H);
temp = temp | 0xffff0000;
__raw_writel(temp,PMU_GRF_GPIO4+PMU_GRF_GPIO4C_IOMUX_H);
temp = temp & (~(0x0000000fU<<12U));
__raw_writel(temp,PMU_GRF_GPIO4+PMU_GRF_GPIO4C_IOMUX_H);
/*A0*/
temp=__raw_readl(PMU_GRF_GPIO4+PMU_GRF_GPIO4C_IOMUX_L);
temp = temp | 0xffff0000;
__raw_writel(temp,PMU_GRF_GPIO4+PMU_GRF_GPIO4C_IOMUX_L);
temp = temp & (~(0x0000000fU<<2U));
__raw_writel(temp,PMU_GRF_GPIO4+PMU_GRF_GPIO4C_IOMUX_L);
/*引脚输入输出模式 可省略与下面重复*/
/*ADC_CLOCK OUT*/
temp=__raw_readl(REGGPIO0+GPIO0_DIR_L);
temp = temp | 0xffff0000;
__raw_writel(temp,REGGPIO0+GPIO0_DIR_L);
temp = temp | ((0x00000001U<<13U));
__raw_writel(temp,REGGPIO0+GPIO0_DIR_L);
/*ADC_DATA_A0 INT*/
temp=__raw_readl(REGGPIO0+GPIO0_DIR_H);
temp = temp | 0xffff0000;
__raw_writel(temp,REGGPIO0+GPIO0_DIR_H);
temp = temp & (~(0x00000001U<<5U));
__raw_writel(temp,REGGPIO0+GPIO0_DIR_H);
/*ADC_DATA_B INT*/
temp=__raw_readl(REGGPIO4+GPIO4_DIR_H);
temp = temp | 0xffff0000;
__raw_writel(temp,REGGPIO4+GPIO4_DIR_H);
temp = temp & (~(0x00000001U<<8U));
__raw_writel(temp,REGGPIO4+GPIO4_DIR_H);
/*ADC_BUSY INT*/
temp=__raw_readl(REGGPIO0+GPIO0_DIR_H);
temp = temp | 0xffff0000;
__raw_writel(temp,REGGPIO0+GPIO0_DIR_H);
temp = temp & (~(0x00000001U<<7U));
__raw_writel(temp,REGGPIO0+GPIO0_DIR_H);
/*ADC_CONVS OUT*/
temp=__raw_readl(REGGPIO0+GPIO0_DIR_H);
temp = temp | 0xffff0000;
__raw_writel(temp,REGGPIO0+GPIO0_DIR_H);
temp = temp | ((0x00000001U<<4U));
__raw_writel(temp,REGGPIO0+GPIO0_DIR_H);
/*M0 OUT */
temp=__raw_readl(REGGPIO4+GPIO4_DIR_H);
temp = temp | 0xffff0000;
__raw_writel(temp,REGGPIO4+GPIO4_DIR_H);
temp = temp | ((0x00000001U<<9U));
__raw_writel(temp,REGGPIO4+GPIO4_DIR_H);
/*M1 OUT*/
temp=__raw_readl(REGGPIO4+GPIO4_DIR_H);
temp = temp | 0xffff0000;
__raw_writel(temp,REGGPIO4+GPIO4_DIR_H);
temp = temp | ((0x00000001U<<7U));
__raw_writel(temp,REGGPIO4+GPIO4_DIR_H);
/*A0 OUT*/
temp=__raw_readl(REGGPIO0+GPIO0_DIR_H);
temp = temp | 0xffff0000;
__raw_writel(temp,REGGPIO4+GPIO4_DIR_H);
temp = temp | ((0x00000001U<<14U));
__raw_writel(temp,REGGPIO0+GPIO0_DIR_H);
/*DISABLE INT*/
/*ADC_CLOCK */
temp=__raw_readl(REGGPIO0+GPIO0_INT_L);
temp = temp | 0xffff0000;
temp = temp & (~(0x00000001U<<13U));
__raw_writel(temp,REGGPIO0+GPIO0_INT_L);
/*ADC_DATA_A0 */
temp=__raw_readl(REGGPIO0+GPIO0_INT_H);
temp = temp | 0xffff0000;
temp = temp & (~(0x00000001U<<5U));
__raw_writel(temp,REGGPIO0+GPIO0_INT_H);
/*ADC_DATA_B*/
temp=__raw_readl(REGGPIO4+GPIO4_INT_H);
temp = temp | 0xffff0000;
temp = temp & (~(0x00000001U<<8U));
__raw_writel(temp,REGGPIO4+GPIO4_INT_H);
/*ADC_BUSY */
temp=__raw_readl(REGGPIO0+GPIO0_INT_H);
temp = temp | 0xffff0000;
temp = temp & (~(0x00000001U<<7U));
__raw_writel(temp,REGGPIO0+GPIO0_INT_H);
/*ADC_CONVS */
temp=__raw_readl(REGGPIO0+GPIO0_INT_H);
temp = temp | 0xffff0000;
temp = temp & (~(0x00000001U<<4U));
__raw_writel(temp,REGGPIO0+GPIO0_INT_H);
/*M0 */
temp=__raw_readl(REGGPIO4+GPIO4_INT_H);
temp = temp | 0xffff0000;
temp = temp & (~(0x00000001U<<9U));
__raw_writel(temp,REGGPIO4+GPIO4_INT_H);
/*M1 */
temp=__raw_readl(REGGPIO4+GPIO4_INT_H);
temp = temp | 0xffff0000;
temp = temp & (~(0x00000001U<<7U));
__raw_writel(temp,REGGPIO4+GPIO4_INT_H);
/*A0 */
temp=__raw_readl(REGGPIO0+GPIO0_INT_H);
temp = temp | 0xffff0000;
temp = temp & (~(0x00000001U<<14U));
__raw_writel(temp,REGGPIO0+GPIO0_INT_H);
}
/**
函數名: set_ADC_CLOCK
函數功能: 设置ADC_CLOCK的输出值
輸入: status: 输出的状态;0--低;1--高
輸出: 无
**/
void set_ADC_CLOCK(unsigned char status)
{
unsigned int temp=0U;
temp=__raw_readl(REGGPIO0+GPIO0_DATA_L);
// temp = temp | ((0x00000001U<<(13U+16U)));
temp = temp | (0xffff0000U);
if(status==0U)
{
// temp = temp & (~(0x00000001U<<13U));
temp = temp & (0xffffdfffU);
}
else
{
// temp = temp | (0x00000001U<<13U);
temp = temp | (0x00002000U);
}
__raw_writel(temp,REGGPIO0+GPIO0_DATA_L);
}
/**
函數名: set_ADC_CONVS
函數功能: 设置ADC_CONVS的输出值
輸入: status: 输出的状态;0--低;1--高
輸出: 无
**/
void set_ADC_CONVS(unsigned char status)
{
unsigned int temp=0U;
temp=__raw_readl(REGGPIO4+GPIO4_DATA_H);
temp = temp | ((0x00000001U<<(2U+16U)));
if(status==0U)
{
// temp = temp & (~(0x00000001U<<2U));
temp = temp & (0xfffffffbU);
}
else
{
// temp = temp | (0x00000001U<<2U);
temp = temp | (0x00000004U);
}
__raw_writel(temp,REGGPIO4+GPIO4_DATA_H);
}
/**
函數名: set_ADC_M0
函數功能: 设置ADC_M0的输出值
輸入: status: 输出的状态;0--低;1--高
輸出: 无
**/
void set_ADC_M0(unsigned char status)
{
unsigned int temp=0U;
temp=__raw_readl(REGGPIO4+GPIO4_DATA_H);
temp = temp | ((0x00000001U<<(9U+16U)));
if(status==0U)
{
// temp = temp & (~(0x00000001U<<9U));
temp = temp & (0xfffffdffU);
}
else
{
// temp = temp | (0x00000001U<<9U);
temp = temp | (0x00000200U);
}
__raw_writel(temp,REGGPIO4+GPIO4_DATA_H);
}
/**
函數名: set_ADC_M1
函數功能: 设置ADC_M1的输出值
輸入: status: 输出的状态;0--低;1--高
輸出: 无
**/
void set_ADC_M1(unsigned char status)
{
unsigned int temp=0U;
temp=__raw_readl(REGGPIO4+GPIO4_DATA_H);
temp = temp | ((0x00000001U<<(7U+16U)));
if(status==0U)
{
// temp = temp & (~(0x00000001U<<7U));
temp = temp & (0xffffff7fU);
}
else
{
// temp = temp | (0x00000001U<<7U);
temp = temp | (0x00000080U);
}
__raw_writel(temp,REGGPIO4+GPIO4_DATA_H);
}
/**
函數名: set_ADC_A0
函數功能: 设置ADC_A0的输出值
輸入: status: 输出的状态;0--低;1--高
輸出: 无
**/
void set_ADC_A0(unsigned char status)
{
unsigned int temp=0U;
temp=__raw_readl(REGGPIO4+GPIO4_DATA_H);
temp |= 0xffff0000U;
if(status==0U)
{
// temp = temp & (~(0x00000001U<<3U));
temp = temp & (0xfffffff7U);
}
else
{
// temp = temp | (0x00000001U<<3U);
temp = temp | (0x00000008U);
}
__raw_writel(temp,REGGPIO4+GPIO4_DATA_H);
}
/**
函數名: get_ADC_DATA_A0
函數功能: 获取ADC的A0引脚的值
輸入: 无
輸出: 获取的状态;0--低;1--高
**/
unsigned char get_ADC_DATA_A0(void)
{
unsigned int temp=0U;
temp=__raw_readl(REGGPIO0+GPIO0_EXT_PORT);
return ((temp>>21U) & 0x00000001U);
}
/**
函數名: get_ADC_DATA_B0
函數功能: 获取ADC的B0引脚的值
輸入: 无
輸出: 获取的状态;0--低;1--高
**/
unsigned char get_ADC_DATA_B0(void)
{
unsigned int temp=0U;
temp=__raw_readl(REGGPIO4+GPIO4_EXT_PORT);
return ((temp>>24U) & 0x00000001U);
}
/**
函數名: get_ADC_BUSY
函數功能: 获取ADC的BUSY引脚的值
輸入: 无
輸出: 获取的状态;0--低;1--高
**/
unsigned char get_ADC_BUSY(void)
{
unsigned int temp=0U;
temp=__raw_readl(REGGPIO0+GPIO0_EXT_PORT);
return ((temp>>23U) & 0x00000001U);
}
/**
* 函数名称 void ADC_init(void)
* 函数功能 ads8361 adc采集模块引脚初始化
* 输入参数 way:采集几路adc
* 函数返回值 无
*/
void ADC_init(unsigned char way)
{
int fd;
unsigned int time_cnt=0;
unsigned int temp=0;
/* 打开 /dev/mem 设备*/
fd = open("/dev/mem", O_RDWR | O_SYNC);
if (fd == -1)
{
return ;
}
/* 映射 GPIO 寄存器到内存*/
PMU_GRF_GPIO0 = (volatile unsigned int *)mmap(NULL, 0X1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, PMU_GRF_GPIO0_BASEADDR);
if (PMU_GRF_GPIO0 == MAP_FAILED)
{
close(fd);
return ;
}
PMU_GRF_GPIO4 = (volatile unsigned int *)mmap(NULL, 0X1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, PMU_GRF_GPIO4_BASEADDR);
if (PMU_GRF_GPIO4 == MAP_FAILED)
{
close(fd);
return ;
}
REGGPIO0 = (volatile unsigned int *)mmap(NULL, 0X1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO0_REG_BASEADDR);
if (REGGPIO0 == MAP_FAILED)
{
close(fd);
return ;
}
REGGPIO4 = (volatile unsigned int *)mmap(NULL, 0X1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO4_REG_BASEADDR);
if (REGGPIO4 == MAP_FAILED)
{
close(fd);
return ;
}
ADC_GPIO_Init();
gpio_init(ADC_collect,ADC_CLOCK);
gpio_init(ADC_collect,ADC_DATA_A);
gpio_init(ADC_collect,ADC_DATA_B);
gpio_init(ADC_collect,ADC_BUSY);
gpio_init(ADC_collect,M0);
gpio_init(ADC_collect,M1);
gpio_init(ADC_collect,A0);
gpio_init(ADC_collect,ADC_CONVST);
set_ADC_CLOCK(0);
set_ADC_CONVS(0);
if(way==2U)
{
set_ADC_M0(0);
set_ADC_M1(0);
}
else
{
set_ADC_M0(1);
set_ADC_M1(0);
}
}
/**
* 函数名称 void ADC_collection2(void)
* 函数功能 ADC采集功能,2路ADC采集功能(将采集到的4路ADC电压值放在voltage_A1,voltage_B1;)
* 输入参数 无
* 函数返回值 无
*/
void ADC_collection2(void)
{
unsigned short data[2]={0U,0U};
unsigned char i,j;
set_ADC_CLOCK(1);
set_ADC_A0(0);
set_ADC_CONVS(1);
// delay_ns(1);
set_ADC_CLOCK(0);
// delay_ns(1);
set_ADC_CLOCK(1);
// delay_ns(1);
set_ADC_CLOCK(0);
// delay_ns(1);
set_ADC_CLOCK(1);
// delay_ns(1);
set_ADC_CLOCK(0);
// delay_ns(1);
for(i=0U;i<16U;i++)
{
data[0] <<= 1;
data[1] <<= 1;
set_ADC_CLOCK(1);
// delay_ns(1);
set_ADC_CLOCK(0);
if(get_ADC_DATA_A0()==1)
{
data[0]++;
}
if(get_ADC_DATA_B0()==1)
{
data[1]++;
}
// delay_ns(1);
}
set_ADC_CLOCK(1);
// delay_ns(1);
set_ADC_CLOCK(0);
// delay_ns(1);
set_ADC_CLOCK(1);
// delay_ns(1);
set_ADC_CLOCK(0);
// delay_ns(1);
while(get_ADC_BUSY()==1)
{
;
}
set_ADC_CONVS(0);
voltage_A1=jisuan(data[0]);//需要优化
voltage_B1=jisuan(data[1]);//需要优化
return;
}
4、总结
这种操作方法目前是接触到驱动开发的操作IO最快的方法了。欢迎大佬留言指导!!!
还有,并不是所有寄存器都可以操作的,在内核上配置的SPI节点,在应用层可以正常使用的,但对其进行寄存器操作,发现不可控。对于这点宝子们先用下面的指令试试寄存器可以控制不,省的写了半天代码,最后是配置问题导致不能用,那可是要命的。
最后,补充一下操作指令:
/* 应用层操作IO */
cd /sys/class/gpio 查看gpio设置,文件,配置
echo 149 > export 将相应的gpio引脚从内核层导入到用户层
echo "in" > direction echo "out" > direction 设置gpio的输入/输出方向
cat value 获取或者控制gpio的状态
echo 1 > value echo 0 > value 设置输出高电平/输出低电平
/* 直接通过寄存器操作IO */
io -4 -r 寄存器地址 通过指令读取寄存器(例:io -4 -r 0xfdd60000)
io -4 -w 寄存器地址 数据 通过指令设置寄存器(例:io -4 -w 0xfdd60000 0x11111111)