现在的位置: 首页 > 综合 > 正文

探索WiringPi 模式1下驱动的实现过程

2013年10月05日 ⁄ 综合 ⁄ 共 4152字 ⁄ 字号 评论关闭

从最简单的gpio实例来分析wiringpi控制io的流程

库的使用有三种方式

int wiringPiSetup (void) ;                //使用IO映射,可以更方便的管理IO   需要root权限
int wiringPiSetupGpio (void) ;      //不用映射直接用物理IO编号                需要root权限
int wiringPiSetupSys (void) ;         //不需要root权限可用脚本直接控制IO)

if (wiringPiSetup () == -1)      //初始化库采用IO映射的方式

这里讨论的是采用IO映射的方式



下面是官方给的实例example 下test1.c

/*
 * test1.c:
 *	一个简单的GPIO实例,来测试wiringpi的函数
 */

#include <wiringPi.h>

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>


// Simple sequencer data
//	Triplets of LED, On/Off and delay

uint8_t data [] =
{
            0, 1, 1,
            1, 1, 1,
  0, 0, 0,  2, 1, 1,
  1, 0, 0,  3, 1, 1,
  2, 0, 0,  4, 1, 1,
  3, 0, 0,  5, 1, 1,
  4, 0, 0,  6, 1, 1,
  5, 0, 0,  7, 1, 1,
  6, 0, 1,
  7, 0, 1,

  0, 0, 1,	// Extra delay

// Back again

            7, 1, 1,
            6, 1, 1,
  7, 0, 0,  5, 1, 1,
  6, 0, 0,  4, 1, 1,
  5, 0, 0,  3, 1, 1,
  4, 0, 0,  2, 1, 1,
  3, 0, 0,  1, 1, 1,
  2, 0, 0,  0, 1, 1,
  1, 0, 1,
  0, 0, 1,

  0, 0, 1,	// Extra delay

  9, 9, 9,	// End marker

} ;


int main (void)
{
  int pin ;
  int dataPtr ;
  int l, s, d ;

  printf ("Raspberry Pi wiringPi test program\n") ;

  if (wiringPiSetup () == -1)      //初始化库采用IO映射的方式
    exit (1) ;

  for (pin = 0 ; pin < 8 ; ++pin)
    pinMode (pin, OUTPUT) ;          //将0-7  8个IO设为输出

  pinMode (8, INPUT) ; 	// 引脚 8设为输入检测按键 SDA0 - Has on-board 2k2 pull-up resistor

  dataPtr = 0 ;

  for (;;)
  {
    l = data [dataPtr++] ;	// LED
    s = data [dataPtr++] ;	// State
    d = data [dataPtr++] ;	// Duration (10ths)

    if ((l + s + d) == 27)
    {
      dataPtr = 0 ;
      continue ;
    }

    digitalWrite (l, s) ; //循环控制IO输出

    if (digitalRead (8) == 0)	// 如果这个引脚GPIO8接地
      delay (d * 10) ;	// Faster!延时变短加速输出
    else
      delay (d * 100) ;
  }

  return 0 ;
}

其中我们关注这几个函数的实现

1,初始化库(有三种方式)

int wiringPiSetup (void) ;                //使用IO映射,可以更方便的管理IO   需要root权限
int wiringPiSetupGpio (void) ;      //不用映射直接用物理IO编号                需要root权限
int wiringPiSetupSys (void) ;         //不需要root权限)

if (wiringPiSetup () == -1)      //初始化库采用IO映射的方式

具体的初始化步骤

进入函数后
1,首先检查用户权限:因为这个函数必须用超级用户权限 getuid() 调用进程的实际用户ID  geteuid() 调用进程的有效用户ID
 if (geteuid () != 0)
  {
    fprintf (stderr, "wiringPi:\n  Must be root to call wiringPiSetup().\n  (Did you forget sudo?)\n") ;
    exit (EXIT_FAILURE) ;
  }
2,然后判断是否进入调试模式
  if (getenv ("WIRINGPI_DEBUG") != NULL)
  {
    printf ("wiringPi: Debug mode enabled\n") ;
    wiringPiDebug = TRUE ;
  }
3,将函数指针和对应的函数连接起来
            pinMode =           pinModeWPi ;
    pullUpDnControl =   pullUpDnControlWPi ;
       digitalWrite =      digitalWriteWPi ;
   digitalWriteByte = digitalWriteByteGpio ;
// Same code
           pwmWrite =          pwmWriteWPi ;
        setPadDrive =       setPadDriveWPi ;
        digitalRead =       digitalReadWPi ;
   waitForInterrupt =  waitForInterruptWPi ;
  delayMicroseconds = delayMicrosecondsWPi ;
         pwmSetMode =        pwmSetModeWPi ;
        pwmSetRange =       pwmSetRangeWPi ;
        pwmSetClock =       pwmSetClockWPi ;

举个例子

<a>声明函数指针:void (*pinMode)           (int pin, int mode) ;

<b>定义一个函数void pinModeWPi (int pin, int mode)
{
  pinModeGpio (pinToGpio [pin & 63], mode) ;
}

<c>将函数指针和函数连接起来
pinMode =           pinModeWPi ;

<d>使用函数
pinMode (8, INPUT) ;
// 引脚 8设为输入检测按键

4,读取树莓派版本信息V1  V2并映射IO口(把IO口顺序整理下方便使用)
boardRev = piBoardRev () ;
  if (boardRev == 1)
    pinToGpio = pinToGpioR1 ;
  else
    pinToGpio = pinToGpioR2 ;

5,接下来就是申请和分配内存,初始化时钟

2,配置IO功能

 pinMode (pin, OUTPUT) ;          //将0-7  8个IO设为输出

pinMode (8, INPUT) ; // 引脚 8设为输入检测按键

由上面的指针函数注册流程可以看出来
pinMode (8, INPUT) ;真实的调用过程是
pinMode (8, INPUT)-----pinModeWPi (int pin, int mode)----pinModeGpio (pinToGpio [pin & 63], mode) ;

所以我们要关注pinModeGpio (pinToGpio [pin & 63], mode) 的实现,这里已经开始操作ARM的寄存器了(63=0011 1111)

定义一个寄存器的过程
(补充下基础知识 在一个数值前加*就相当于取这个地址里面的数值 uchar p=200; value=*p)
#define BCM2708_PERI_BASE                    0x20000000  //寄存器在ARM中的起始地址
#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) //GPIO_BASE寄存器起始地址

static volatile uint32_t *gpio ;      //定义一个值随时可能变化的指针gpio指向那个需要操作的寄存器

//用户空间映射
 gpio = (uint32_t *)mmap((caddr_t)gpioMem, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fd, GPIO_BASE) ;
gpio指向了GPIO_BASE  这样就可以直接操作*gpio就等于修改了GPIO_BASE寄存器的值

最终的使用
 *(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) ; // Sets bits to zero = input

pinModeGpio (pinToGpio [pin & 63], mode) ;函数内容

//  register int barrier ;

  int fSel, shift, alt ;

  pin &= 63 ;

  fSel    = gpioToGPFSEL [pin] ; //计算GPIO位偏移量
  shift   = gpioToShift  [pin] ;   //设置某一位的偏移量

  /**/ if (mode == INPUT)
    *(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) ; // 将输入输出控制位置0=做输入用
  else if (mode == OUTPUT)
    *(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) | (1 << shift) ;  //置1做输出
  else if (mode == PWM_OUTPUT)
  {
    if ((alt = gpioToPwmALT [pin]) == 0) // Not a PWM pin
      return ;

3,IO数据输入/输出

digitalWrite (l, s) ; //循环控制IO输出

if (digitalRead (8) == 0) // 如果这个引脚GPIO8接地

数字IO口的读写函数实现流程和上面的流程大致是一样的只不过操作了不同的寄存器而已 
digitalWrite (l, s)        //控制输出
if (digitalRead (8) == 0) //检测输入

函数先是声明一个函数指针
实现一个函数
然后让指针指向这个函数
digitalWrite =      digitalWriteWPi ;

void digitalWriteWPi (int pin, int value)
{
  pin = pinToGpio [pin & 63] ;

  if (value == LOW)
    *(gpio + gpioToGPCLR [pin]) = 1 << (pin & 31) ;
  else
    *(gpio + gpioToGPSET [pin]) = 1 << (pin & 31) ;
}

抱歉!评论已关闭.