滴滴FAE

各种调屏经验分享:TFT,AMOLED,PMOLED,详情见麦瑞科技术mrklcd.com

RM67162 1.2英寸AMOLED调试 390*390

0
阅读(5580)

    X120BLN02这款屏可以用SPI、MIPI、SPI+MIPI这三款通讯方式,但为了省主控的成本,不得不用SPI来驱动,如果主控的SPI时钟太慢,势必会影响到刷新效果。

先看硬件,接口定义如下,从接口可以看出,FPC上自带POWER IC(BGA的)

image.png

image.png

//以下代码是4W-SPI

//IC:RM67162

//Module:H120BLN02

//interface:4W-SPI,

//主控SPI时钟32MHZ

//色深:RGB332,之前我一直以为方案会用RGB565,为是没有减少数据,那只能降色深,说真的RGB332效果还

//不赖,毕竟是Amoled,手表又不像手机,眼睛一直盯着看

//但是有个问题无法接触:SPI速率太低,同时平台在刷每帧都是全屏刷,所以在切换画面时会有延迟感

//我有建议方案改UI,即只有第一幅画面全屏刷,其余的都是写窗口,只要窗口像素点在肉眼能识别的时间内刷完

//不影响用户体验,还有个办法就是换高配置的主控

//方案驱动工程师有建议采用Vertical Scrolling刷屏,把IC规格书翻烂了,都没有找到这个寄存器

//所以只能放弃滚屏,话说ST7789V可以滚屏,不过我还没有试过,有机会领教一下,滚屏用来规避画面刷新

//延迟还是蛮不错的


#include "nrf_drv_spi.h"

#include "nrf_gpio.h"

#include "dxc_hal.h"

#include "dxc_halLcd.h"

#include "drv_rm67162.h"


// @brief 设置命令模式

static __INLINE void rm6716x_set_command_mode(void)

{

    nrf_gpio_pin_clear(RM6716X_CMD_DATA_PIN);

    delay_us(22);

}


// @brief 设置数据模式

static __INLINE void rm6716x_set_data_mode(void)

{

    nrf_gpio_pin_set(RM6716X_CMD_DATA_PIN);

    delay_us(22);

}


// @brief 发送数据

void rm6716x_write_data(const uint8_t* data, uint16_t dataLen)

{

    rm6716x_spi_csn_clr();

    rm6716x_set_data_mode();

    rm6716x_transfer(data, dataLen, NULL, 0);

    rm6716x_spi_csn_set();

}


// @brief 发送命令和数据

void rm6716x_send_command_data(uint8_t cmd, const uint8_t* data, uint16_t dataLen)

{

    rm6716x_spi_csn_clr();

    rm6716x_set_command_mode();

    rm6716x_transfer(&cmd, 1, NULL, 0);

    if (data && dataLen > 0)

    {

        rm6716x_set_data_mode();

        rm6716x_transfer(data, dataLen, NULL, 0);

    }

    rm6716x_spi_csn_set();

}


// @brief 发送命令和字节

static __INLINE void rm6716x_send_command_byte(uint8_t cmd, uint8_t byte)

{

    rm6716x_send_command_data(cmd, &byte, 1);

}


// @brief 发送命令

static __INLINE void rm6716x_write_command(uint8_t cmd)

{

    rm6716x_send_command_data(cmd, NULL, 0);

}


uint16_t mem_start_address;             // 垂直滚动地址

int16_t mem_length = 390;                     // 内存长(左右长度)

int16_t mem_width = 390;                      // 内存宽(上下高度)


// @brief 接口调试

void rm6716x_who_am_i(void)

{

    uint8_t buff[8]; 

    memset(buff, 0, sizeof(buff));

    buff[0] = 0x04;

    rm6716x_transfer(buff, 1, &buff[1], 3);

    Debug("buff: %02x-%02x-%02x-%02x-%02x-%02x", buff[0], buff[1], buff[2], buff[3], buff[4], buff[5]);

}


void rm6716x_x120bln_init(void)

{

    uint8_t buff[32];

    uint8_t offset;


    // 引脚初始化

    hal_gpio_cfg_output(RM6716X_POWER_ENABLE_PIN);

    hal_gpio_cfg_output(RM6716X_SPI_RESET_PIN);

    hal_gpio_cfg_output(RM6716X_CMD_DATA_PIN);


    // power enable

    hal_gpio_pin_set(RM6716X_POWER_ENABLE_PIN);

    delay_ms(15);


    // reset

    rm6716x_reset();

    

    // 初始化

    rm6716x_spi_init();

    delay_ms(2);

    

    rm6716x_send_command_byte(0xfe, 0x01); // page 0

    rm6716x_send_command_byte(0x6c, 0x0a); // mipi turn off

    rm6716x_send_command_byte(0x04, 0xa0);// 开启 spi 写 ram,此04必需写0xA0才能打开SPIR

    rm6716x_send_command_byte(0xfe, 0x05);//Page4

    rm6716x_send_command_byte(0x05, 0x00);

    rm6716x_send_command_byte(0xfe, 0x00);//User Command

    rm6716x_send_command_byte(0x35, 0x00);

    rm6716x_send_command_byte(0x36, RM6716X_MADCTL_MY|RM6716X_MADCTL_MX|RM6716X_MADCTL_RGB);

    rm6716x_send_command_byte(RM6716X_CMD_SET_BRIGHTNESS, 200);

    rm6716x_send_command_byte(0x53, 0x20);//关dimming

    rm6716x_send_command_byte(0xc4, 0x80);

    rm6716x_send_command_byte(0x3a, RM6716X_RGB_FORMAT_565);


    offset = 0;

    buff[offset++] = 0x00;

    buff[offset++] = 0x00;

    buff[offset++] = 0x01;          // 0x00ef = 239; 319 = 0x013f

    buff[offset++] = 0x85;

    rm6716x_send_command_data(RM6716X_CMD_COLUMN_ADDR_SET, buff, offset);

    offset = 0;

    buff[offset++] = 0x00;

    buff[offset++] = 0x00;

    buff[offset++] = 0x01;          // 0x00ef = 239; 319 = 0x013f

    buff[offset++] = 0x85;

    rm6716x_send_command_data(RM6716X_CMD_ROW_ADDR_SET, buff, offset);


    rm6716x_write_command(0x11);            // sleep out

    delay_ms(120);

    

rm6716x_write_command(0x29);//display on

delay_ms(20);

rm6716x_write_command(0x2C);//write Gram

delay_ms(20);

    rm6716x_show_block_color(0, 0, 389, 389, WHITE);

}



// @brief 寄存器初始化

void rm6716x_init(void)

{

    // gpio 初始化

    hal_gpio_cfg_output(RM6716X_POWER_ENABLE_PIN);

    hal_gpio_cfg_output(RM6716X_SPI_RESET_PIN);

    hal_gpio_cfg_output(RM6716X_CMD_DATA_PIN);

    rm6716x_spi_init();

    delay_ms(1);

    nrf_gpio_pin_clear(RM6716X_SPI_RESET_PIN);

    delay_ms(20);

    nrf_gpio_pin_set(RM6716X_SPI_RESET_PIN);

    delay_ms(120);

    

    // reg config

    rm6716x_write_command(RM6716X_CMD_SLEEP_OUT);

    delay_ms(10);

}


// @brief 使能电源

void rm6716x_power_enable(void)

{

    // 复位引脚拉低

    hal_gpio_cfg_output(RM6716X_SPI_RESET_PIN);

    hal_gpio_pin_clear(RM6716X_SPI_RESET_PIN);

    hal_gpio_cfg_output(RM6716X_CMD_DATA_PIN);

    

    // 电源芯片引脚拉高

    hal_gpio_cfg_output(RM6716X_POWER_ENABLE_PIN);

    hal_gpio_pin_set(RM6716X_POWER_ENABLE_PIN);

}


// 初始化 spi

void rm6716x_spi_enable(void)

{

    rm6716x_spi_init();

}


void rm6716x_reset(void)

{

    hal_gpio_cfg_output(RM6716X_SPI_RESET_PIN);

    delay_us(20);

    nrf_gpio_pin_set(RM6716X_SPI_RESET_PIN);

    delay_ms(4);

    nrf_gpio_pin_clear(RM6716X_SPI_RESET_PIN);

    delay_ms(4);

    nrf_gpio_pin_set(RM6716X_SPI_RESET_PIN);

    delay_ms(12);

}


// @brief 进入休眠

void rm6716x_enter_sleep(void)

{

    rm6716x_write_command(RM6716X_CMD_DISPLAY_OFF);

    rm6716x_write_command(RM6716X_CMD_SLEEP_IN);

}


// @brief 退出休眠

void rm6716x_exit_sleep(void)

{

    rm6716x_write_command(RM6716X_CMD_SLEEP_OUT);

    delay_ms(5);

    rm6716x_write_command(RM6716X_CMD_DISPLAY_ON);

}


// @brief 设置窗口地址

// @param xs,xe 设置列区域(左右),ys,ye 设置行区域(上下);

void rm6716x_set_addr_window(uint16_t xs, uint16_t ys, uint16_t xe, uint16_t ye)

{

    uint8_t buff[4];

    

    buff[0] = xs >> 8;

    buff[1] = xs;

    buff[2] = xe >> 8;

    buff[3] = xe;

    rm6716x_send_command_data(RM6716X_CMD_COLUMN_ADDR_SET, buff, 4);


    buff[0] = ys >> 8;

    buff[1] = ys;

    buff[2] = ye >> 8;

    buff[3] = ye;

    rm6716x_send_command_data(RM6716X_CMD_ROW_ADDR_SET, buff, 4);

    

    rm6716x_write_command(RM6716X_CMD_MEM_WRITE);

}


// @brief 显示一个区块的颜色

bool rm6716x_show_block_color(int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color)

{

    uint16_t i;

    uint16_t length, weight;

    uint8_t buff[256];


    if (x2 > mem_length - 1)

        x2 = mem_length - 1;

    if (y2 > mem_length - 1)

        y2 = mem_length - 1;

    if (x1 < 0)

        x1 = 0;

    if (y1 < 0)

        y1 = 0;

    

    if ((x1 > x2) || (y1 > y2))

    {

        return FALSE;

    }


    length = x2-x1+1; 

    weight = y2-y1+1;

    

    for (i=0; i<=256;) 

    {

        buff[i++] = color >> 8;

        buff[i++] = color;

    }


    // 对某个区域内存进行写

    rm6716x_set_addr_window(x1, y1, x2, y2);

    for(i=0; i<weight; i++)

    {

        if (length < 128)

        {

            rm6716x_write_data(buff, (length << 1));

        }

        else 

        {

            rm6716x_write_data(buff, length);

            rm6716x_write_data(buff, length);

        }

    }

rm6716x_write_command(0x2C);

delay_ms(20);

    return TRUE;

}


// @brief 显示一张图片的一部分

// @param x1,y1 显示坐标

// @param xoft,yoft 图片显示偏移量

// @return true 成功显示图片;false 失败,没有显示图片

bool rm6716x_show_picture(int16_t x1, int16_t y1, int16_t xoft, int16_t yoft, const uint8_t *data)

{

    int16_t i;

    int16_t length, width;

    uint8_t buf[256];

    uint32_t offset = 8;

    int16_t xLen, yLen; 

    

    // 如果坐标小于0,需要做偏移

    if (x1 < 0)

    {

        xoft += (-x1);

        x1 = 0;

    }

    if (y1 < 0)

    {

        yoft += (-y1);

        y1 = 0;

    }

    length = ((data[2] << 8) | (data[3] & 0xFF));

    width = ((data[4] << 8) | (data[5] & 0xFF));

    if (xoft > length - 1 || yoft > width - 1)      // 偏移超出图片大小

    {

        Log("The error picture position. xoft=%d, yoft=%d, width=%d, length=%d", xoft, yoft, width, length);

        return FALSE;

    }

    

    xLen = length - xoft;

    yLen = width - yoft;

    xoft = 0;

    if (x1 + xLen > mem_length - 1)

        xLen = mem_length - x1;

    else

        xoft = (length - xLen) * 2;

    length <<= 1;

    if (y1 + yLen > mem_width - 1)

        yLen = mem_width - y1;

    else

        offset += yoft * length;

    rm6716x_set_addr_window(x1, y1, x1 + xLen - 1, y1 + yLen - 1);

    for (i=0; i<yLen; i++)

    {

        if (xLen < 128)

        {

            memcpy(buf, &data[offset + xoft], (xLen << 1));

            rm6716x_write_data(buf, (xLen << 1));

        }

        else 

        {

            memcpy(buf, &data[offset + xoft], xLen);

            rm6716x_write_data(buf, xLen);

            memcpy(buf, &data[offset+xLen + xoft], xLen);

            rm6716x_write_data(buf, xLen);

        }

        

        offset += length;

    }

    return TRUE;

}


// @brief 垂直滚动定义命令

// @param tfix 顶部固定区域;scoll 滚动区域;bfix 底部固定区域

// @note: tfix + scoll + bfix = 320

void rm6716x_vertical_scrolling_defined(uint16_t tfix, uint16_t scoll, uint16_t bfix)

{

    uint8_t data[6];


    data[0] = tfix >> 8;

    data[1] = tfix;

    data[2] = scoll >> 8;

    data[3] = scoll;

    data[4] = bfix >> 8;

    data[5] = bfix;

    LCD_SendCmdData(RM6716X_CMD_VERTICAL_SCROLLING, data, 6);

}


// @brief设置垂直滚动起始地址

void rm6716x_set_mem_start_address(uint16_t addr)

{

    uint8_t data[2];


    if (addr == mem_start_address)

        return;

    mem_start_address = addr;

    data[0] = addr >> 8;

    data[1] = addr;

    LCD_SendCmdData(RM6716X_CMD_VER_SCRO_START_ADDR, data, 2);

}