lynnen

lynnen的博客

她的个人主页  她的博客

EInk显示屏 waveform文件的分析

lynnen  2011年02月07日 星期一 15:55 | 6959次浏览 | 0条评论

        本人这些年也算是做过几个俗话说是嵌入式系统的人,可以说做嵌入试系统其实就是做3大块。第一大块是存储,以NandFlash , SD卡为代表,现在是Emmc Flash(4.4版),做存储加上要考虑boot的问题,要做文件系统。 第二大块是广义的网络连接,包括串口,USB,WiFi, 3G 。 第三大块就是用户I/O, 主要就是显示技术了,对用户来说最直观看得着的就是显示屏了。

       这几年我也开发及调试过不少显示屏,其中最有特色,最难调试的就算EInk屏了。为什么? 第一,它要有专门的电源管理配合,其中还有一个VCOM,不仅要用I2C寄存器去控制它的开关,还要用GPIO(GPIO4_21)去控制它是否加载到 EInk上。其二,就是它要有一个Waveform文件,就是波形文件去配合它,它是电子墨水运动轨迹的定义。

      在I.MX5的内核中带了一个默认的Waveform文件,以6寸屏为例,文件在  {kernel}/firmware/imx/epdc_E60_V110.fw,  下面分析一下它的结构。

      epdc_e60_v110.fw 文件 = 一个48字节的头 + 14字节温度项 + 1字节间隔 + 全部数据

用hexedit 来看

 $hexedit  epdc_e60_v110.fw

      再结合kernel中EInk的驱动来看,kernel中EInk的驱动在 {kernel}/drivers/video/mxc/mxc_epdc_fb.c , 其中读取waveform文件,并加载数据的部分在 mxc_epdc_fb_fw_handler(...)中,waveform头结构的定义在前面 struct waveform_data_header 中

struct waveform_data_header {
    unsigned int wi0;
    unsigned int wi1;
    unsigned int wi2;
    unsigned int wi3;
    unsigned int wi4;
    unsigned int wi5;
    unsigned int wi6;
    unsigned int xwia:24;
    unsigned int cs1:8;
    unsigned int wmta:24;
    unsigned int fvsn:8;
    unsigned int luts:8;
    unsigned int mc:8;
    unsigned int trc:8;
    unsigned int reserved0_0:8;
    unsigned int eb:8;
    unsigned int sb:8;
    unsigned int reserved0_1:8;
    unsigned int reserved0_2:8;
    unsigned int reserved0_3:8;
    unsigned int reserved0_4:8;
    unsigned int reserved0_5:8;
    unsigned int cs2:8;
};

struct mxcfb_waveform_data_file {
    struct waveform_data_header wdh;
    u32 *data;    /* Temperature Range Table + Waveform Data */
};

static void mxc_epdc_fb_fw_handler(const struct firmware *fw,
                             void *context)
{
    struct mxc_epdc_fb_data *fb_data = context;
    int ret;
    struct mxcfb_waveform_data_file *wv_file;
    int wv_data_offs;
    int i;
    struct mxcfb_update_data update;

     ....   //空指针处理,略

     wv_file = (struct mxcfb_waveform_data_file *)fw->data;

    /* Get size and allocate temperature range table */

    fb_data->trt_entries = wv_file->wdh.trc + 1;  //这里wdh.trc就是0Dh, =13 , 所以trt_entries = 14,温度项算14个字节。

    fb_data->temp_range_bounds = kzalloc(fb_data->trt_entries, GFP_KERNEL);  //这里分配14个字节的空间给温度项保存,即 temp_range_bounds

    for (i = 0; i < fb_data->trt_entries; i++)
        dev_dbg(fb_data->dev, "trt entry #%d = 0x%x\n", i, *((u8 *)&wv_file->data + i));   //把温度项14个字节打印出来。

    /* Copy TRT data */
    memcpy(fb_data->temp_range_bounds, &wv_file->data, fb_data->trt_entries);   //把温度项14个字节装到temp_range_bounds内存块中。

    /* Set default temperature index using TRT and room temp */
    fb_data->temp_index = mxc_epdc_fb_get_temp_index(fb_data, DEFAULT_TEMP);

    /* Get offset and size for waveform data */
    wv_data_offs = sizeof(wv_file->wdh) + fb_data->trt_entries + 1;  //最后,计算真正的data偏移位置,= 48字节的wdh + 14字节的trt_entries + 1 = 63;

    fb_data->waveform_buffer_size = fw->size - wv_data_offs;   //data块的大小 = waveform文件的大小 - data偏移 = waveform文件大小 - 63 ;


    /* Allocate memory for waveform data */
    fb_data->waveform_buffer_virt = dma_alloc_coherent(fb_data->dev,
                        fb_data->waveform_buffer_size,
                        &fb_data->waveform_buffer_phys,
                        GFP_DMA);    //给waveform数据分配内存块,大小=waveform文件大小 - 63

// dma内存分配调用完后得到两个地址,一个是虚拟地址,一个是物理地址,两个是对应的,虚拟地址给linux程序用,物理地址用来写入硬件寄存器。

    if (fb_data->waveform_buffer_virt == NULL) {
        dev_err(fb_data->dev, "Can't allocate mem for waveform!\n");
        return;
    }

    memcpy(fb_data->waveform_buffer_virt, (u8 *)(fw->data) + wv_data_offs,
        fb_data->waveform_buffer_size);     //将数据装入分配好的内存块

    release_firmware(fw);   //数据已经装入内存块了,释放waveform文件。

    /* Enable clocks to access EPDC regs */
    clk_enable(fb_data->epdc_clk_axi);

    /* Enable pix clk for EPDC */
    clk_enable(fb_data->epdc_clk_pix);
    clk_set_rate(fb_data->epdc_clk_pix, fb_data->cur_mode->vmode->pixclock);

    epdc_init_sequence(fb_data);

    /* Disable clocks */
    clk_disable(fb_data->epdc_clk_axi);
    clk_disable(fb_data->epdc_clk_pix);

    fb_data->hw_ready = true;

    update.update_region.left = 0;
    update.update_region.width = fb_data->info.var.xres;
    update.update_region.top = 0;
    update.update_region.height = fb_data->info.var.yres;
    update.update_mode = UPDATE_MODE_FULL;
    update.waveform_mode = WAVEFORM_MODE_AUTO;
    update.update_marker = INIT_UPDATE_MARKER;
    update.temp = TEMP_USE_AMBIENT;
    update.flags = 0;

    mxc_epdc_fb_send_update(&update, &fb_data->info);

    /* Block on initial update */
    ret = mxc_epdc_fb_wait_update_complete(update.update_marker,
        &fb_data->info);
    if (ret < 0)
        dev_err(fb_data->dev,
            "Wait for update complete failed.  Error = 0x%x", ret);
}

评论

我的评论:

发表评论

请 登录 后发表评论。还没有在Zeuux哲思注册吗?现在 注册 !

暂时没有评论

Zeuux © 2024

京ICP备05028076号