anhy0923
HY's Embedded LAB
anhy0923
전체 방문자
오늘
어제
  • 분류 전체보기 (67)
    • UDS 진단통신 (1)
    • FreeRTOS (2)
    • ROS2 (5)
    • [EH전공] CAN 통신 (1)
    • C언어 튜터링: C로 로봇을 파헤쳐보자! (4)
    • [졸업작품] 외벽 균열 검사 로봇 (8)
      • 2021-2 <종합설계기획> (7)
      • 2022-1 <종합설계1> (1)
      • 2022-2 <종합설계2> (0)
    • TCP 기반 제어시스템 (7)
      • Robot Arm Control (5)
      • RPi CCTV Server (2)
    • Embedded System (6)
      • Linux (3)
      • Linux - Ubuntu (2)
      • RPi (1)
    • Drone FW using STM32CubeIDE (25)
      • 0. Intro (2)
      • 1. Debug (3)
      • 2. Sensor Interface (4)
      • 3. GPS (4)
      • 4. Transmitter_Receiver (3)
      • 5. Drone Body Asb (1)
      • 6. ESC Protocol (4)
      • 7. EEPROM (2)
      • 8. GCS (1)
      • PID Control (1)
    • Elec Academy (3)
    • HAL & LL Driver (1)
    • OpenCV - Lane Detection (1)

블로그 메뉴

  • 홈
  • 방명록

티스토리

hELLO · Designed By 정상우.
anhy0923

HY's Embedded LAB

fpga_interface_driver.c 분석
Embedded System/Linux

fpga_interface_driver.c 분석

2021. 11. 16. 02:06

 

/*----------------------------------------

프로그램 설명:

라즈베리파이와 FPGA간의 interface를 위한 디바이스 드라이버 프로그램

----------------------------------------*/

/* FPGA LED Ioremap Control

FILE : fpga_fpga_itf_driver.c*/

/*----------------------------------------

사용되는 각종 헤더 파일의 정의

----------------------------------------*/

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/delay.h>

#include <linux/interrupt.h>

#include <linux/gpio.h>

#include <asm-generic/bitsperlong.h>

 

 

/*----------------------------------------

nWE: Write Enable (active low)

nOE: Output Enable (active low)

nCS: Chip Select (active low)

----------------------------------------*/

#define CTRL_nWE 0  

#define CTRL_nOE 1

#define CTRL_nCS 2

 

/*----------------------------------------

라즈베리 파이와 FPGA 간 Memory Mapping이 아닌 GPIO를 이용한 DATA 통신을 이용한다.

FPGA_Interface_driver에서 GPIO를 이용하여 address 12bit 와 data 8bit를 타이밍에 따라 FPGA로 보낸다. address 12bit와 data 8bit 로 해당하는 device에 write/read 한다.

 

Linux/gpio.h 헤더파일 안을 보면 gpio 구조체는 다음과 같이 정의되어 있다.

 

 

-> gpio 구조체

 

 

다음과 같이 정의 되어 있으므로 GPIOF_OUT_INIT_LOW 는 0이다.

 

 

 

 

 

 

 

 

 

 

-> Raspberry pi 27pin maps for FPGA interface

 

----------------------------------------*/

 

/*----------------------------------------

gpio 구조체 타입의 배열 iom_fpga_address에 Raspberry pi Address pin 에 해당하는 pin번호,

GPIOF_OUT_INIT_LOW (0), label 값을 gpio 구조체로 묶어 선언한다.

----------------------------------------*/

static struct gpio iom_fpga_address[] = {  // A1 ~ A11, A0=LOW

    /*{ 10, GPIOF_OUT_INIT_LOW, "ADDRESS 00" },*/ { 11, GPIOF_OUT_INIT_LOW, "ADDRESS 01" },

    { 12, GPIOF_OUT_INIT_LOW, "ADDRESS 02" }, { 13, GPIOF_OUT_INIT_LOW, "ADDRESS 03" },

    { 14, GPIOF_OUT_INIT_LOW, "ADDRESS 04" }, { 15, GPIOF_OUT_INIT_LOW, "ADDRESS 05" },

    { 16, GPIOF_OUT_INIT_LOW, "ADDRESS 06" }, { 17, GPIOF_OUT_INIT_LOW, "ADDRESS 07" },

    { 18, GPIOF_OUT_INIT_LOW, "ADDRESS 08" }, { 19, GPIOF_OUT_INIT_LOW, "ADDRESS 09" },

    { 20, GPIOF_OUT_INIT_LOW, "ADDRESS 10" }, { 21, GPIOF_OUT_INIT_LOW, "ADDRESS 11" },

};

 

/*----------------------------------------

gpio 구조체 타입의 배열 iom_fpga_data에 Raspberry pi Data pin 에 해당하는 pin번호,

GPIOF_OUT_INIT_LOW (0), label 값을 gpio 구조체로 묶어 선언한다.

----------------------------------------*/

static struct gpio iom_fpga_data[] = {

    { 2, GPIOF_OUT_INIT_LOW, "DATA 0" }, { 3, GPIOF_OUT_INIT_LOW, "DATA 1" },

    { 4, GPIOF_OUT_INIT_LOW, "DATA 2" }, { 5, GPIOF_OUT_INIT_LOW, "DATA 3" },

    { 6, GPIOF_OUT_INIT_LOW, "DATA 4" }, { 7, GPIOF_OUT_INIT_LOW, "DATA 5" },

    { 8, GPIOF_OUT_INIT_LOW, "DATA 6" }, { 9, GPIOF_OUT_INIT_LOW, "DATA 7" },

};

 

/*----------------------------------------

gpio 구조체 타입의 배열 iom_fpga_control에 Raspberry pi Control pin 에 해당하는 pin번호,

GPIOF_OUT_INIT_LOW (0), label 값을 gpio 구조체로 묶어 선언한다.

nWE: Write Enable (active low)

nOE: Output Enable (active low)

nCS: Chip Select (active low)

----------------------------------------*/

static struct gpio iom_fpga_control[] = {

    { 22, GPIOF_OUT_INIT_LOW, "nWE" },

    { 23, GPIOF_OUT_INIT_LOW, "nOE" },

    { 25, GPIOF_OUT_INIT_LOW, "nCS" },

};

 

/*----------------------------------------

Raspberry pi의 Address 핀, Data 핀, Control 핀을 default로 설정해주는 함수이다. (초기설정)

Iom_fpga_itf_open( ) 함수 끝에서 실행 된다.

----------------------------------------*/

static void iom_fpga_itf_set_default(void)

{

    int i = 0;

                           // A0 핀은 항상 LOW

    gpio_set_value(10, 0); // A0: always set to LOW

   

    // Address 핀이 저장되어있는 gpio타입 구조체 배열 iom_fpga_address의 원소들의 값을

      모두 0 (LOW) 으로 설정

    for (i=0; i<ARRAY_SIZE(iom_fpga_address); i++) {

        gpio_set_value(iom_fpga_address[i].gpio, 0);

    }

   

// Data 핀이 저장되어있는 gpio타입 구조체 배열 iom_fpga_data의 원소들의 값을

      모두 0 (LOW) 으로 설정

    for (i=0; i<ARRAY_SIZE(iom_fpga_data); i++) {

        gpio_set_value(iom_fpga_data[i].gpio, 0);

    }

 

// Control 핀이 저장되어있는 gpio타입 구조체 배열 iom_fpga_control의 원소들의 값을

      모두 1 (HIGH) 으로 설정

    for (i=0; i<ARRAY_SIZE(iom_fpga_control); i++) {

        gpio_set_value(iom_fpga_control[i].gpio, 1);

    }

}

 

 

/*----------------------------------------

FPGA를 처음 구동시킬 때 이 함수를 호출

----------------------------------------*/

static int iom_fpga_itf_open(void)

{

    int ret = 0;

 

    // iom_fpga_address 배열에서 에러가 있는 Address 핀 번호를 출력 및 해당 핀 번호 return

    ret = gpio_request_array(iom_fpga_address, ARRAY_SIZE(iom_fpga_address));

    if (ret) {

        printk(KERN_ERR "Unable to request address GPIOs: %d\n", ret);

        return ret;

    }

 

    // iom_fpga_data 배열에서 에러가 있는 Data 핀 번호를 출력 및 해당 핀 번호 return

    ret = gpio_request_array(iom_fpga_data, ARRAY_SIZE(iom_fpga_data));

    if (ret) {

        printk(KERN_ERR "Unable to request data GPIOs: %d\n", ret);

        return ret;

    }

 

    // iom_fpga_control 배열에서 에러가 있는 control 핀 번호를 출력 및 해당 핀 번호 return

    ret = gpio_request_array(iom_fpga_control, ARRAY_SIZE(iom_fpga_control));

    if (ret) {

        printk(KERN_ERR "Unable to request control GPIOs: %d\n", ret);

        return ret;

    }

 

    iom_fpga_itf_set_default();  // 모든 핀이 정상 상태이면 핀 초기 설정을 해주는 함수 호출

    return ret;

}

 

/*----------------------------------------

FPGA를 사용하지 않을 때, 이 함수를 호출

----------------------------------------*/

static int iom_fpga_itf_release(void)

{

    iom_fpga_itf_set_default();  // 핀 초기 설정 함수 호출

   

    // Address, Data, Control 핀이 저장된 구조체 배열 등록 해제

    gpio_free_array(iom_fpga_address, ARRAY_SIZE(iom_fpga_address));

    gpio_free_array(iom_fpga_data, ARRAY_SIZE(iom_fpga_data));

    gpio_free_array(iom_fpga_control, ARRAY_SIZE(iom_fpga_control));

 

    return 0;

}

 

 

/*----------------------------------------

데이터를 쓰기 위해 GPIO를 타이밍 제어 하는 함수

RPi3에서 FPGA에 Data를 Write 할 때 동작 순서는 다음과 같다.

1. Address와 Data에 해당하는 GPIO pin에 주소와 데이터 신호를 인가한다.

2. nCS에 해당하는 GPIO pin에 Low를 인가한다.

3. nWE에 해당하는 GPIO pin에 Low를 5us 정도 인가한다.

-> nWE 신호가 Low로 인가되어 있는 동안 FPGA에서 주소와 데이터를 읽어 간다.

따라서 Address와 Data는 nCS가 LOW로 되어있는 동안 유지되어야 한다.

----------------------------------------*/

ssize_t iom_fpga_itf_write(unsigned int addr, unsigned char value)

{

    size_t length = 1;

    int i = 0;

 

 

    // 어느 주소에 어떤 데이터를 쓰는지 출력

    printk("FPGA WRITE: address = 0x%x, data = 0x%x \n", addr, value);

 

    /* 아래 for문이 작동하는 방식

    */

    for (i=0; i<ARRAY_SIZE(iom_fpga_address); i++) {

         // 데이터를 쓸 FPGA 주소의 2진수 값을 GPIO 주소 핀에 출력

        gpio_set_value(iom_fpga_address[i].gpio, (addr >> i) & 0x1);

    }

 

    for (i=0; i<ARRAY_SIZE(iom_fpga_data); i++) {

         // FPGA에 쓸 데이터의 2진수 값을 GPIO 데이터 핀에 출력

        gpio_set_value(iom_fpga_data[i].gpio, (value >> i) & 0x1);

    }

   

    // write를 위해 Control 핀 nCS, nWE 핀에 Low 인가

    // nWE 가 Low 로 인가되어 있는 동안 FPGA에서 주소와 데이터를 읽어 감

    gpio_set_value(iom_fpga_control[CTRL_nCS].gpio, 0); udelay(1);

    gpio_set_value(iom_fpga_control[CTRL_nWE].gpio, 0); udelay(5);

    //printk("CS:%d, ", gpio_get_value(iom_fpga_control[CTRL_nCS].gpio));

    //printk("WE:%d, ", gpio_get_value(iom_fpga_control[CTRL_nWE].gpio));

    //printk("\n");

 

    // Control 핀 High 로 초기화

    gpio_set_value(iom_fpga_control[CTRL_nWE].gpio, 1);

    gpio_set_value(iom_fpga_control[CTRL_nCS].gpio, 1);

 

    /*

    // Debugging...

    for (i=0; i<ARRAY_SIZE(iom_fpga_address); i++) {

        printk("Address(%d):%d, ", i, gpio_get_value(iom_fpga_address[i].gpio));

    }

 

    printk("\n");

    for (i=0; i<ARRAY_SIZE(iom_fpga_data); i++) {

        printk("Data(%d):%d, ", i, gpio_get_value(iom_fpga_data[i].gpio));

    }

 

    printk("\n");

    printk("CS:%d, ", gpio_get_value(iom_fpga_control[CTRL_nCS].gpio));

    printk("WE:%d, ", gpio_get_value(iom_fpga_control[CTRL_nWE].gpio));

    printk("\n");

    */

 

    return length; // 해당 IO 함수의 성공여부를 리턴

}

EXPORT_SYMBOL(iom_fpga_itf_write); // 커널 심볼 테이블 등록 함수 : 커널의 다른 드라이버가 사용함

 

 

 

 

/*----------------------------------------

데이터를 읽어오기 위해 GPIO를 타이밍 제어하는 함수

RPi3에서 FPGA에 Data를 Read 할 때 동작 순서는 다음과 같다.

1. Address와 Data에 해당하는 GPIO pin에 FPGA Device의 주소를 인가한다.

2. nCS에 해당하는 GPIO pin에 LOW를 인가한다.

3. nOE에 해당하는 GPIO pin에 LOW를 인가한다.

-> nOE 신호가 Low로 인가되어 있는 동안 데이터를 읽어 온다.

따라서 Address는 nCS가 Low로 인가되어 있는 동안 유지되어야 한다.

----------------------------------------*/

unsigned char iom_fpga_itf_read(unsigned int addr)

{

    unsigned char value = 0;

    int i = 0;

   

    // 데이터를 쓸 FPGA 주소의 2진수 값을 GPIO 주소 핀에 출력

    for (i=0; i<ARRAY_SIZE(iom_fpga_address); i++) {

        gpio_set_value(iom_fpga_address[i].gpio, (addr >> i) & 0x1);

    }

 

    // Read를 위해 Control 핀 nCS, nOE 핀에 Low 인가

    // nOE 신호가 Low로 인가되어 있는 동안 데이터를 읽어 옴

    gpio_set_value(iom_fpga_control[CTRL_nCS].gpio, 0); udelay(1);

    gpio_set_value(iom_fpga_control[CTRL_nOE].gpio, 0); udelay(1);

 

    // nOE 신호가 Low로 인가되어 있는 동안 데이터를 읽어 value에 저장

    for (i=0; i<ARRAY_SIZE(iom_fpga_data); i++) {

        value += gpio_get_value(iom_fpga_data[i].gpio) << i;

    }

 

    // Control 핀 High 로 초기화

    gpio_set_value(iom_fpga_control[CTRL_nCS].gpio, 1);

    gpio_set_value(iom_fpga_control[CTRL_nOE].gpio, 1);

   

// 해당 주소에서 읽은 데이터 출력

    printk("FPGA READ: address = 0x%x, data = 0x%x \n", addr, value);

 

    return value; // 해당 주소에서 읽은 데이터 반환

}

EXPORT_SYMBOL(iom_fpga_itf_read); // 커널 심볼 테이블 등록 함수 : 커널의 다른 드라이버가 사용함

 

// module_init 함수에 의해 한번 호출되는 함수, iom_fpga_itf_open( ) 함수를 실행

int __init iom_fpga_itf_init(void)

{

    printk("init module: %s\n", __func__);

    iom_fpga_itf_open();

    return 0;

}

 

// module_exit 함수에 의해 한번 호출되는 함수, iom_fpga_itf_release( ) 함수를 실행

void __exit iom_fpga_itf_exit(void)

{

    printk("exit module: %s\n", __func__);

    iom_fpga_itf_release();

}

 

 

/*----------------------------------------

 module_init 매크로는 커널에게 모듈이 로딩되었을 때 호출되어야 하는 함수를 알려주는 역할을 한다.

  그리고 모듈이 하게 되는 모든 일들이 바로 이 초기화 함수가 호출하는 함수에 의해서 처리가 되게

된다.

---------------------------------------*/

module_init(iom_fpga_itf_init);

 

/*----------------------------------------

module_exit 매크로는 커널에게 모듈을 언로드 될때 호출되어야 하는 함수를 알려주는 역할을 한다.

---------------------------------------*/

module_exit(iom_fpga_itf_exit);

 

 

/*----------------------------------------

MODULE_LICENSE 매크로는 커널에게 모듈이 어떤 라이선스 하에서 커널을 이용하게 되는지를 알려주는

역할을 한다. 사용하는 라이선스에 따라서 사용할 수 있는 심볼(함수 혹은 변수 등)에 제한이 생기게 된다.

---------------------------------------*/

MODULE_LICENSE("GPL");

저작자표시 비영리 (새창열림)

'Embedded System > Linux' 카테고리의 다른 글

[Embedded System - Linux] 리눅스 명령어  (0) 2021.09.21
[Embedded System - Linux] 리눅스의 종류  (0) 2021.09.21
    'Embedded System/Linux' 카테고리의 다른 글
    • [Embedded System - Linux] 리눅스 명령어
    • [Embedded System - Linux] 리눅스의 종류
    anhy0923
    anhy0923

    티스토리툴바