2017年9月3日 星期日

使用mmap函式映射Memory的方式來操作GPIO

ref: Here

使用mmap函式映射Memory的方式來操作GPIO,達到高速存取的目的,比起傳統的sysfs更為有效率,使得後面其他功能更為Real time,首先創建gpio.h,宣告函式的原型
gpio.h
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sched.h>
#include <sys/mman.h>

#define MMIO_SUCCESS 0
#define MMIO_ERROR_DEVMEM -1
#define MMIO_ERROR_MMAP -2

#define BASE 0xB01B0000
#define GPIO_INC 3
#define GPIO_OUTEN 0
#define GPIO_INEN 1
#define GPIO_DAT 2
#define GPIO_LENGTH (0x254+1) (GPIO_END_ADDR - GPIO_START_ADDR)
#define GPIO_LENGTH 528 (?) (GPIO_END_ADDR - GPIO_START_ADDR)   

/*
ref1: kernel/arch/arm/mach-owl/include/mach/gpio.h
//GPIOA/B/C/D/E, GPIOE0~4
#define NR_OWL_GPIO
ref2: kernel/arch/arm/mach-owl/gpio.owi.h
owl_gpio_chip.ngpio
*/

int s500_mmio_init(void);
void s500_mmio_set_input(int gpio_number);
void s500_mmio_set_output(int gpio_number);
void s500_mmio_set_high(int gpio_number);
void s500_mmio_set_low(int gpio_number);
uint32_t s500_mmio_read_input(int gpio_number);

void set_max_priority(void);
void set_default_priority(void);
----------------------------------------
其中大部分都是GPIO常用的功能,和程式的Priority,其中BASE,GPIO_INC,GPIO_OUTEN,GPIO_INEN,GPIO_DAT和GPIO_LENGTH由Action S500的Datasheet來決定。因為每3個Word(32 Bit)為一個GPIO Bank,因此設置累加為3,即GPIO_INC,而每個Bank中OUT的offset為0,IN為1,DAT為2,即可完成對於任何GPIO number找到對應的Register Address來進行操作


gpio.c
#include "gpio.h"
volatile uint32_t* s500_mmio_gpio = NULL;

int s500_mmio_init(void) {
  if (s500_mmio_gpio == NULL) {
    int fd = open("/dev/mem", O_RDWR | O_SYNC);
    if (fd == -1) {
      // Error opening /dev/mem.  Probably not running as root.
      return MMIO_ERROR_DEVMEM;
    }
    // Map GPIO memory to location in process space.
    s500_mmio_gpio = (uint32_t*)mmap(NULL, GPIO_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, fd, BASE);
    close(fd);
    if (s500_mmio_gpio == MAP_FAILED) {
      // Don't save the result if the memory mapping failed.
      s500_mmio_gpio = NULL;
      return MMIO_ERROR_MMAP;
    }
  }
  return MMIO_SUCCESS;
}

void s500_mmio_set_input(int gpio_number) {
  *(s500_mmio_gpio + ((gpio_number)/32)*GPIO_INC + GPIO_INEN) |= (1<<((gpio_number)%32));
  *(s500_mmio_gpio + ((gpio_number)/32)*GPIO_INC + GPIO_OUTEN) &= ~(1<<((gpio_number)%32));
}

void s500_mmio_set_output(int gpio_number) {
  *(s500_mmio_gpio + ((gpio_number)/32)*GPIO_INC + GPIO_INEN) &= ~(1<<((gpio_number)%32));
  *(s500_mmio_gpio + ((gpio_number)/32)*GPIO_INC + GPIO_OUTEN) |= (1<<((gpio_number)%32)); 
}

void s500_mmio_set_high(int gpio_number) {
  *(s500_mmio_gpio + ((gpio_number)/32)*GPIO_INC + GPIO_DAT) |= (1<<((gpio_number)%32));
}

void s500_mmio_set_low(int gpio_number) {
  *(s500_mmio_gpio + ((gpio_number)/32)*GPIO_INC + GPIO_DAT) &= ~(1<<((gpio_number)%32));
}

uint32_t s500_mmio_read_input(int gpio_number) {
  return (*(s500_mmio_gpio + ((gpio_number)/32)*GPIO_INC + GPIO_DAT) & (1<<((gpio_number)%32)) ? 1 : 0);
}

void set_max_priority(void) {
  struct sched_param sched;
  memset(&sched, 0, sizeof(sched));
  // Use FIFO scheduler with highest priority for the lowest chance of the kernel context switching.
  sched.sched_priority = sched_get_priority_max(SCHED_FIFO);
  sched_setscheduler(0, SCHED_FIFO, &sched);
}

void set_default_priority(void) {
  struct sched_param sched;
  memset(&sched, 0, sizeof(sched));
  // Go back to default scheduler with default 0 priority.
  sched.sched_priority = 0;
  sched_setscheduler(0, SCHED_OTHER, &sched);
}
---------------------------------------
main.c

#include <stdio.h>
#include <stdlib.h>
#include "gpio.h"

void main (void)
{
    if (s500_mmio_init() < 0)
        printf("error open mem!n");

    set_max_priority();
    s500_mmio_set_output(IN1);
    set_default_priority();
}
ref: 【LeMaker Guitar試用體驗】居家水族箱

沒有留言:

張貼留言