FC2カウンター FPGAの部屋 ZedBoard Linux のフレームバッファにカメラ画像を表示15(Linux既存のFBを使用)
FC2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

ZedBoard Linux のフレームバッファにカメラ画像を表示15(Linux既存のFBを使用)

前のブログ記事は、”ZedBoard Linux のフレームバッファにカメラ画像を表示14(ChipScope波形)

”AXI4 Master IP にAXI4 Lite Slave を追加10(ハードウェア完成)”で、AXI4 Master バスと AXI4 Lite Slave バスを持つXPSプロジェクトのカスタムIPを作ることが出来た。今回は、Cプログラムを作って起動時にLinuxのペンギンが写っている既存のフレームバッファをアドレスを取得して、カメラ・インターフェイスIPとビットマップ・ディスプレイ・コントローラIPのフレームバッファ・スタート・アドレス・レジスタにWrite する。

SDKで、cam_disp3_linux プロジェクトの cam_disp3_linux.c を変更した。(以前の cam_disp3_linux.c のコードは、”ZedBoard Linux のフレームバッファにカメラ画像を表示11(SDKリモートデバック3)”に貼ってある)
ZedBoard_Linux2_2_130421.png

下にCソースコードを貼っておく。

// cam_disp3_linux.c
// 
// GPIOを1にして、カメラ表示回路を生かし、MT9D111の設定レジスタにRGB565を設定する
//
// 2013/02/11
// 2013/04/20 : カメラ・インターフェイスIPとビットマップ・ディスプレイ・コントローラIPのフレームバッファ・スタート・レジスタに
//                Linuxの既存のフレームバッファのアドレスをWrite するように変更。
//

#define XPAR_AXI_GPIO_0_BASEADDR        0x44000000
#define XPAR_AXI_IIC_MT9D111_BASEADDR    0x45000000

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
#include <assert.h>
#include <ctype.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <unistd.h>

#define PAGE_SIZE (4*1024)
#define BLOCK_SIZE (4*1024)

#define BUFSIZE    1024

// I/O access

volatile unsigned *setup_io(off_t addr);
int chkhex(char *str);

volatile unsigned *cam_i2c_control_reg;
volatile unsigned *cam_i2c_status_reg;
volatile unsigned *cam_i2c_tx_fifo;
volatile unsigned *cam_i2c_rx_fifo;

void cam_i2c_init(void) {
    *cam_i2c_control_reg = 0x2// reset tx fifo
    *cam_i2c_control_reg = 0x1// enable i2c
}

void cam_i2x_write_sync(void) {
    // unsigned c;

    // c = *cam_i2c_rx_fifo;
    // while ((c & 0x84) != 0x80)
        // c = *cam_i2c_rx_fifo; // No Bus Busy and TX_FIFO_Empty = 1
    usleep(1000);
}

void cam_i2c_write(unsigned int device_addr, unsigned int write_addr, unsigned int write_data){
    *cam_i2c_tx_fifo = 0x100 | (device_addr & 0xfe);    // Slave IIC Write Address
    *cam_i2c_tx_fifo = write_addr;
    *cam_i2c_tx_fifo = (write_data >> 8)|0xff;            // first data
    *cam_i2c_tx_fifo = 0x200 | (write_data & 0xff);        // second data
    cam_i2x_write_sync();
}

int main()
{
    volatile unsigned *cam_i2c, *axi_gpio;
    volatile unsigned *axi_gpio_tri;
    FILE *fd;
    char buf[BUFSIZE], *token;
    char *str0x;
    unsigned int read_num;
    unsigned int mt9d111_reg_addr, bitmap_dc_reg_addr;
    volatile unsigned *mt9d111_inf_reg;
    volatile unsigned *bm_disp_cnt_reg;
    unsigned int fb_addr;
    unsigned int val;
    
    cam_i2c = setup_io((off_t)XPAR_AXI_IIC_MT9D111_BASEADDR);
    axi_gpio = setup_io((off_t)XPAR_AXI_GPIO_0_BASEADDR);
    
    cam_i2c_control_reg = cam_i2c + 0x40// 0x100番地
    cam_i2c_status_reg = cam_i2c + 0x41// 0x104番地
    cam_i2c_tx_fifo = cam_i2c + 0x42// 0x108番地
    cam_i2c_rx_fifo = cam_i2c + 0x43// 0x10C番地
    
    axi_gpio_tri = axi_gpio + 0x1// 0x4番地
    
    // フレームバッファのアドレスを取得
    memset(buf, '\0'sizeof(buf)); // buf すべてに\0 を入れる
    // dmesg で、fbi->fix.smem_start の書いてある行をパイプに入れる
    fd = popen("dmesg | grep \"fbi->fix.smem_start\"""r");
    if (fd != NULL){ // fbi->fix.smem_start の値を抽出
        read_num = fread(buf, sizeof(unsigned char), BUFSIZE, fd);
        if (read_num > 0){
            if ((str0x = strstr(buf, "0x")) != NULL){
                str0x += 2;
                sscanf(str0x, "%x\n", &fb_addr);
            }
        }
    }
    pclose(fd);

    // mt9d111-inf-axi-master と  bitmap-disp-cntrler-axi-master のアドレスを取得
    memset(buf, '\0'sizeof(buf)); // buf すべてに\0 を入れる
    // ls /sys/devices/axi.0 の内容をパイプに入れる
    fd = popen("ls /sys/devices/axi.0""r");
    if (fd != NULL){
        read_num = fread(buf, sizeof(unsigned char), BUFSIZE, fd);
        if (read_num > 0){
            token = buf;
            if ((token=strtok(token, ".\n")) != NULL){
                do {
                    if (chkhex(token)){ // 16進数
                        sscanf(token, "%x", &val);
                    } else {
                        if (strcmp(token, "mt9d111-inf-axi-master") == 0)
                            mt9d111_reg_addr = val;
                        else if (strcmp(token, "bitmap-disp-cntrler-axi-master") == 0)
                            bitmap_dc_reg_addr = val;
                    }
                }while((token=strtok(NULL, ".\n")) != NULL);
            }
        }
    }
    pclose(fd);

    mt9d111_inf_reg = setup_io((off_t)mt9d111_reg_addr);
    bm_disp_cnt_reg = setup_io((off_t)bitmap_dc_reg_addr);

    // フレームバッファのアドレスをAXI4 Lite Slave 経由でレジスタにWrite
    fb_addr = fb_addr + (140 * 1920 * 4); // 2頭のペンギンが表示されるようにペンギンの描画領域を外す。140ライン飛ばす
    *bm_disp_cnt_reg = fb_addr;
    *mt9d111_inf_reg = fb_addr;

    // GPIOに1を書いて、カメラ表示回路を動作させる
    *axi_gpio_tri = 0;    // set output
    *axi_gpio = 0x1;    // output '1'
    
    // CMOS Camera initialize, MT9D111
    cam_i2c_init();
    
    cam_i2c_write(0xba, 0xf00x1);        // IFP page 1 へレジスタ・マップを切り替える
    cam_i2c_write(0xba, 0x970x20);    // RGB Mode, RGB565
    
    return(0);
}

//
// Set up a memory regions to access GPIO
//
volatile unsigned *setup_io(off_t mapped_addr)
// void setup_io()
{
    int  mem_fd;
    char *gpio_mem, *gpio_map;

   /* open /dev/mem */
   if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
      printf("can't open /dev/mem \n");
      exit (-1);
   }

   /* mmap GPIO */

   // Allocate MAP block
   if ((gpio_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) {
      printf("allocation error \n");
      exit (-1);
   }

   // Make sure pointer is on 4K boundary
   if ((unsigned long)gpio_mem % PAGE_SIZE)
     gpio_mem += PAGE_SIZE - ((unsigned long)gpio_mem % PAGE_SIZE);

   // Now map it
   gpio_map = (unsigned char *)mmap(
      (caddr_t)gpio_mem,
      BLOCK_SIZE,
      PROT_READ|PROT_WRITE,
      MAP_SHARED|MAP_FIXED,
      mem_fd,
      mapped_addr
   );

   if ((long)gpio_map < 0) {
      printf("mmap error %d\n", (int)gpio_map);
      exit (-1);
   }

   // Always use volatile pointer!
   // gpio = (volatile unsigned *)gpio_map;
   return((volatile unsigned *)gpio_map);

// setup_io

// 文字列が16進数かを調べる
int chkhex(char *str){
    while (*str != '\0'){
        if (!isxdigit(*str))
            return 0;
        str++;
    }
    return 1;
}


なお、このCプログラム作成にあたって、ツィッターで色々と教えていただいた。ありがとうございました。

これで、このソフトウェアを動作させると、Linuxのペンギンが写っている既存のフレームバッファをアドレスを取得して、カメラ・インターフェイスIPとビットマップ・ディスプレイ・コントローラIPのフレームバッファ・スタート・アドレス・レジスタにWrite する。そして、フレームバッファとして使用している領域を活用するので、Linuxのワークエリアを乗っ取ることがなくなり、Linuxが飛ぶことが無くなるはずだ。
Cプログラムが完成したので、”ZedBoard Linux のフレームバッファにカメラ画像を表示12(SDカードのイメージ変更)”を参照して、cam_disp3_linux.elf をSDカードに書き込んだ。
ZedBoard_Linux2_1_130421.png

これで起動時に、変更した cam_disp3_linux.elf が起動する。”ZedBoard Linux のフレームバッファにカメラ画像を表示”プロジェクトはこれでとりあえず完成とする。
後は、自分でフレームバッファを作製して、そこにカメラの画像を写すという目標もある。
HDMIを自分のビットマップ・ディスプレイ・コントローラIPから出力してみたい。
  1. 2013年04月21日 04:49 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
http://marsee101.blog.fc2.com/tb.php/2445-e91272fc
この記事にトラックバックする(FC2ブログユーザー)