1. 问题描述
目前在手写笔项目中需要使用一块LCD来显示个人信息和当前工作状态等
2. 问题分析
2.1 芯片资料
查看数据说手册中的图,看到如何
看到这个数据格式为:【地址+控制字+数据字】
从 m 和 n 的格式上看:是支持一次从批量的数据的
- 地址需要看SA0的接线方式:
- 控制就是读写数据位有效 参考代码为 0x00 和 0x40
屏幕厂商的电路图如下:
厂商给的驱动程序参考:
// 各个引脚对应的高低由单片机IO口控制
#ifdef Interface_IIC
/**********************************************
//
//IIC通信开始函数
//
**********************************************/
void IC_IIC_Start()
{
SDA = high;
SCL = high;
_nop_();
SDA = low;
_nop_();
_nop_();
SCL = low;
}
/**********************************************
//
//IIC通信停止函数
//
**********************************************/
void IC_IIC_Stop()
{
SDA = low;
_nop_();
SCL = high;
_nop_();
_nop_();
SDA = high;
}
/**********************************************
//
//向IIC写数据函数
//返回值为acknowledgement位信号
//
**********************************************/
bit Write_IIC_Data(uchar Data)
{
unsigned char i;
bit Ack_Bit; //应答信号
for (i = 0; i < 8; i++)
{
SDA = (bit)(Data & 0x80);
_nop_();
SCL = high;
_nop_();
_nop_();
SCL = low;
Data = _crol_(Data, 1);
}
SDA = high; //释放IIC SDA总线为主器件接收从器件产生应答信号
_nop_();
_nop_();
SCL = high; //第9个时钟周期
_nop_();
_nop_();
Ack_Bit = SDA; //读取应答信号
SCL = low;
return Ack_Bit;
}
/**********************************************
//
//写命令函数
**********************************************/
void Write_Command(uchar command)
{
IC_IIC_Start();
Write_IIC_Data(Slave_Address); //Salve Adress
Write_IIC_Data(OP_Command); //写命令
Write_IIC_Data(command);
IC_IIC_Stop();
}
/**********************************************
//
//写数据函数
//
**********************************************/
void Write_Data(uchar date)
{
IC_IIC_Start();
Write_IIC_Data(Slave_Address); //Salve Adress
Write_IIC_Data(OP_Data); //写数据
Write_IIC_Data(date);
IC_IIC_Stop();
}
#endif
void init_program()
{
Write_Command(0xAE); //Display OFF (sleep mode)
Write_Command(0xD5); //Set Display Clock Divide Ratio/Oscillator Frequency
Write_Command(0x52); //320kHz //101hz 52
Write_Command(0xA8); //Set Multiplex Ratio
Write_Command(0x0f); //16
Write_Command(0xD3); //Set Display Offset
Write_Command(0x00); //
Write_Command(0x40); //Set Display Start Line
Write_Command(0x8D); //Set Display Offset
Write_Command(0x14); //VCC Generated by Internal DC/DC Circuit
Write_Command(0xA0); //Set Segment Re-map
Write_Command(0xC0); //Set COM Output Scan Direction
Write_Command(0xDA); //Set COM Pins Hardware Configuration
Write_Command(0x02); //
Write_Command(0x81); //Set Contrast Control
Write_Command(0x30);
Write_Command(0xD9); //Set Pre-charge Period
Write_Command(0xf1); //VCC Supplied Externally
Write_Command(0xDB); //Set VCOMH Deselect Level
Write_Command(0x20); //0x3C
Write_Command(0xA4); //Output follows RAM content
Write_Command(0xA6); //Set Normal Display
display_white();
Write_Command(0xAF); //Set Display ON (normal mode)
}
void Reset_IC()
{
Delay_Ms(10);
// LED_Work = low;
RES = low;
Delay_Ms(50);
RES = high;
Delay_Ms(100);
// VCC_Change = high;
}
void display_white(void)
{
unsigned int j, i;
for (i = 0xB0; i < 0xB2; i++)
{
Write_Command(i);
Write_Command(0x00);
Write_Command(0x12);
for (j = 0; j < 96; j++)
Write_Data(0xff);
}
}
void main()
{
Reset_IC();
init_program();
display_white();
}
2.2 51822 驱动移植
此芯片使用的II接口的方式驱动,在51822的资料中使用的是TWI接口
- 库文件
- 打开库的配置宏 (此处参考库的demo中关于配置的打开)
// <e> TWI_ENABLED - nrf_drv_twi - TWI/TWIM peripheral driver
//==========================================================
#ifndef TWI_ENABLED
#define TWI_ENABLED 1
#endif
#if TWI_ENABLED
// <o> TWI_DEFAULT_CONFIG_FREQUENCY - Frequency
// <26738688=> 100k
// <67108864=> 250k
// <104857600=> 400k
#ifndef TWI_DEFAULT_CONFIG_FREQUENCY
#define TWI_DEFAULT_CONFIG_FREQUENCY 26738688
#endif
// <q> TWI_DEFAULT_CONFIG_CLR_BUS_INIT - Enables bus clearing procedure during init
#ifndef TWI_DEFAULT_CONFIG_CLR_BUS_INIT
#define TWI_DEFAULT_CONFIG_CLR_BUS_INIT 0
#endif
// <q> TWI_DEFAULT_CONFIG_HOLD_BUS_UNINIT - Enables bus holding after uninit
#ifndef TWI_DEFAULT_CONFIG_HOLD_BUS_UNINIT
#define TWI_DEFAULT_CONFIG_HOLD_BUS_UNINIT 0
#endif
// <o> TWI_DEFAULT_CONFIG_IRQ_PRIORITY - Interrupt priority
// <i> Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice
// <0=> 0 (highest)
// <1=> 1
// <2=> 2
// <3=> 3
#ifndef TWI_DEFAULT_CONFIG_IRQ_PRIORITY
#define TWI_DEFAULT_CONFIG_IRQ_PRIORITY 3
#endif
// <e> TWI0_ENABLED - Enable TWI0 instance
//==========================================================
#ifndef TWI0_ENABLED
#define TWI0_ENABLED 1
#endif
#if TWI0_ENABLED
// <q> TWI0_USE_EASY_DMA - Use EasyDMA (if present)
#ifndef TWI0_USE_EASY_DMA
#define TWI0_USE_EASY_DMA 0
#endif
#endif //TWI0_ENABLED
// </e>
// <e> TWI1_ENABLED - Enable TWI1 instance
//==========================================================
#ifndef TWI1_ENABLED
#define TWI1_ENABLED 0
#endif
#if TWI1_ENABLED
// <q> TWI1_USE_EASY_DMA - Use EasyDMA (if present)
#ifndef TWI1_USE_EASY_DMA
#define TWI1_USE_EASY_DMA 0
#endif
#endif //TWI1_ENABLED
// </e>
// <e> TWI_CONFIG_LOG_ENABLED - Enables logging in the module.
//==========================================================
#ifndef TWI_CONFIG_LOG_ENABLED
#define TWI_CONFIG_LOG_ENABLED 0
#endif
#if TWI_CONFIG_LOG_ENABLED
// <o> TWI_CONFIG_LOG_LEVEL - Default Severity level
// <0=> Off
// <1=> Error
// <2=> Warning
// <3=> Info
// <4=> Debug
#ifndef TWI_CONFIG_LOG_LEVEL
#define TWI_CONFIG_LOG_LEVEL 3
#endif
// <o> TWI_CONFIG_INFO_COLOR - ANSI escape code prefix.
// <0=> Default
// <1=> Black
// <2=> Red
// <3=> Green
// <4=> Yellow
// <5=> Blue
// <6=> Magenta
// <7=> Cyan
// <8=> White
#ifndef TWI_CONFIG_INFO_COLOR
#define TWI_CONFIG_INFO_COLOR 0
#endif
// <o> TWI_CONFIG_DEBUG_COLOR - ANSI escape code prefix.
// <0=> Default
// <1=> Black
// <2=> Red
// <3=> Green
// <4=> Yellow
// <5=> Blue
// <6=> Magenta
// <7=> Cyan
// <8=> White
#ifndef TWI_CONFIG_DEBUG_COLOR
#define TWI_CONFIG_DEBUG_COLOR 0
#endif
#endif //TWI_CONFIG_LOG_ENABLED
// </e>
#endif //TWI_ENABLED
// <q> APP_TWI_ENABLED - app_twi - TWI transaction manager
#ifndef APP_TWI_ENABLED
#define APP_TWI_ENABLED 1
#endif
- 配置初始化接口
/*******************************************************************************
* Function Name : sxb_lcd_init
* Description : 手写笔 LCD 接口初始化
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void sxb_lcd_bsp_init(void)
{
uint32_t ret;
/* 初始化IIC总线 */
nrf_drv_twi_config_t const config = {
.scl = LCD_SCL_PIN,
.sda = LCD_SDA_PIN,
.frequency = NRF_TWI_FREQ_400K,
.interrupt_priority = APP_IRQ_PRIORITY_LOWEST,
.clear_bus_init = false,
};
APP_TWI_INIT(&m_app_twi, &config, MAX_PENDING_TRANSACTIONS, ret);
APP_ERROR_CHECK(ret);
/* 初始化自己的管脚 */
nrf_gpio_cfg_output(LCD_RST_PIN);
nrf_gpio_pin_clear(LCD_RST_PIN);
nrf_gpio_pin_set(LCD_RST_PIN);
}
要想使用TWI接口需要了解的核心结构体
typedef struct {
uint8_t * p_data; ///< Pointer to the buffer holding the data.
uint8_t length; ///< Number of bytes to transfer.
uint8_t operation; ///< Device address combined with transfer direction.
uint8_t flags; ///< Transfer flags (see @ref APP_TWI_NO_STOP).
} app_twi_transfer_t;
typedef struct {
app_twi_callback_t callback;
///< User-specified function to be called after the transaction is finished.
void * p_user_data;
///< Pointer to user data to be passed to the callback.
app_twi_transfer_t const * p_transfers;
///< Pointer to the array of transfers that make up the transaction.
uint8_t number_of_transfers;
///< Number of transfers that make up the transaction.
} app_twi_transaction_t;
app_twi_transaction_t : 这个结构体定义N次读写操作
app_twi_transfer_t:一次读写操作配置
写一次数据对应的用法如下:
/*******************************************************************************
* Function Name : lcd_write_cmd_cb
* Description : LCD 写命令接口(是缓存接口,不能连续使用)
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void lcd_write_cmd(uint8_t cmd)
{
m_lcd_sbuf[0] = LCD_REG_C;
m_lcd_sbuf[1] = cmd;
/* 设置每个序列的内容 */
lcd_transfers.operation = APP_TWI_WRITE_OP(LCD_ADDR);
lcd_transfers.p_data = m_lcd_sbuf;
lcd_transfers.length = 2;
lcd_transfers.flags = 0;
/* 设置传输序列 */
lcd_transaction.callback = lcd_write_cmd_cb;
lcd_transaction.p_user_data = m_lcd_sbuf + 1;
lcd_transaction.p_transfers = &lcd_transfers;
lcd_transaction.number_of_transfers = 1;
APP_ERROR_CHECK(app_twi_schedule(&m_app_twi, &lcd_transaction));
}
写多条数据对应的操作如下:
/*******************************************************************************
* Function Name : sxb_lcd_bsp_setting
* Description : LCD 配置显示参数接口
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void sxb_lcd_bsp_setting(void)
{
uint8_t i;
/* 设置初始化序列 */
for (i = 0; i <= 21; i++)
{
init_transfers[i].operation = APP_TWI_WRITE_OP(LCD_ADDR);
init_transfers[i].p_data = (uint8_t *)&lcd_init_setting[i];
init_transfers[i].length = 2;
if (i == 21)
init_transfers[i].flags = APP_TWI_NO_STOP;
else
init_transfers[i].flags = 0;
}
/* 设置传输序列 */
lcd_transaction.callback = lcd_setting_cb;
lcd_transaction.p_user_data = NULL;
lcd_transaction.p_transfers = init_transfers;
lcd_transaction.number_of_transfers = 22;
/* 启动传输 */
APP_ERROR_CHECK(app_twi_schedule(&m_app_twi, &lcd_transaction));
}
注意:发送指令的数据缓存需要单独存起来,这个参数在启动传输之前都没有实际的数据交互,在回调执行前不能随意修改缓存的数据,否则可能会导致数据异常
同厂商沟通过可以采用批量写数据
/*******************************************************************************
* Function Name : sxb_lcd_dis_screen
* Description : LCD 刷屏函数
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void sxb_lcd_dis_screen(void)
{
uint8_t i;
static lcd_cmd_t lcd_dat_setting[8];
/* 刷新LCD */
for (i = 0; i < 2; i++)
{
// cmd 0xbx
lcd_dat_setting[4 * i + 0].type = LCD_REG_C;
lcd_dat_setting[4 * i + 0].cmd = 0xb0 + i;
init_transfers[4 * i + 0].operation = APP_TWI_WRITE_OP(LCD_ADDR);
init_transfers[4 * i + 0].p_data = (uint8_t *)&lcd_dat_setting[4 * i + 0];
init_transfers[4 * i + 0].length = 2;
init_transfers[4 * i + 0].flags = APP_TWI_NO_STOP;
// cmd 0x00
lcd_dat_setting[4 * i + 1].type = LCD_REG_C;
lcd_dat_setting[4 * i + 1].cmd = 0;
init_transfers[4 * i + 1].operation = APP_TWI_WRITE_OP(LCD_ADDR);
init_transfers[4 * i + 1].p_data = (uint8_t *)&lcd_dat_setting[4 * i + 1];
init_transfers[4 * i + 1].length = 2;
init_transfers[4 * i + 1].flags = APP_TWI_NO_STOP;
// cmd 0x12
lcd_dat_setting[4 * i + 2].type = LCD_REG_C;
lcd_dat_setting[4 * i + 2].cmd = 0x12;
init_transfers[4 * i + 2].operation = APP_TWI_WRITE_OP(LCD_ADDR);
init_transfers[4 * i + 2].p_data = (uint8_t *)&lcd_dat_setting[4 * i + 2];
init_transfers[4 * i + 2].length = 2;
init_transfers[4 * i + 2].flags = APP_TWI_NO_STOP;
/* 这个是设置刷新缓存 */
sxb_lcd_tcb.pbuf[97 * i] = LCD_REG_D;
init_transfers[4 * i + 3].operation = APP_TWI_WRITE_OP(LCD_ADDR);
init_transfers[4 * i + 3].p_data = sxb_lcd_tcb.pbuf + 97 * i;
init_transfers[4 * i + 3].length = 97; // 这个数据是批量送的
init_transfers[4 * i + 3].flags = APP_TWI_NO_STOP;
}
/* 设置传输序列 */
lcd_transaction.callback = lcd_screen_cb;
lcd_transaction.p_user_data = NULL;
lcd_transaction.p_transfers = init_transfers;
lcd_transaction.number_of_transfers = 8;
/* 启动传输 */
APP_ERROR_CHECK(app_twi_schedule(&m_app_twi, &lcd_transaction));
}