FC2カウンター FPGAの部屋 ZedBoard Linux上でカメラの画像を処理する2(準備編2)
FC2ブログ

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

FPGAの部屋

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

ZedBoard Linux上でカメラの画像を処理する2(準備編2)

ZedBoardでカメラ画像処理をするために、以前やってきたことを”ZedBoard Linux上でカメラの画像を処理する1(準備編)”でまとめた。
今回は、フレーム・バッファのアドレスをファイルに書いておくようにするのが目的だ。ZedBoardのLinux起動時に、Linuxのペンギンが写っている既存のフレームバッファをアドレスを取得して、カメラ・インターフェイスIPとビットマップ・ディスプレイ・コントローラIPのフレームバッファ・スタート・アドレス・レジスタにWrite するようなCソフトウェアを実行している。そのソフトウェアでフレーム・バッファの開始アドレスをfb_start_addr.txt というファイルに書いておくようにしようと思う。そのために起動時のCソフトウェアを変更した。
今のところ、SDKリモートデバッグ経由だが、ZedBoardのLinux上で起動して、fb_start_addr.txt に開始アドレスが入るのを確認できた。
image_process_Zed_linux_1_130916.png

0x19106800 番地がフレーム・バッファのスタートアドレスだ。アドレスが中途半端なのは、2頭のペンギンが表示されるように、多分Linuxが描画しているペンギンの描画領域を外すで、140ライン飛ばしているからだ。
dmesg コマンドで元のフレーム・バッファのアドレスを見ると、fbi->fix.smem_start = 0x19000000 となっていて、0x19000000 をフレーム・バッファに指定していることがわかる。
image_process_Zed_linux_2_130916.png

下に、変更した cam_disp3_linux.c を示す。元の cam_disp3_linux.c は、”ZedBoard Linux のフレームバッファにカメラ画像を表示15(Linux既存のFBを使用)”を参照のこと。
(2013/09/25:修正)フレーム・バッファのスタートアドレスを4Kバイトのページ境界に修正した。

// 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)) & (~(int)(PAGE_SIZE-1))) + PAGE_SIZE;
    // 2頭のペンギンが表示されるようにペンギンの描画領域を外す。140ライン飛ばす、更にページ境界に合わせる
    *bm_disp_cnt_reg = fb_addr;
    *mt9d111_inf_reg = fb_addr;

    // fb_start_addr.txt に fb_addr を書き込む
    sprintf(buf, "echo %x > /Apps/fb_start_addr.txt", fb_addr);
    system(buf);
    
    // 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);
   }

   close(mem_fd);
   // 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;
}


なお、echoコマンドをsystem() コマンドで実行することで、fb_start_addr.txt ファイルを作成しているが、fopen() で、fb_start_addr.txt ファイルをオープンして、fprintf() でフレーム・バッファのスタートアドレスを書き込もうとしたら、ソフトウェアが強制終了されてしまった。これはなぜだろう?
これは後で、SDカードに書き込むことになるが、その際には、”ZedBoard Linux のフレームバッファにカメラ画像を表示12(SDカードのイメージ変更)”を参考にして、SDカードに書き込もうと思う。
  1. 2013年09月16日 05:20 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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