【项目笔记_手写笔】在51822 上移植SSD1306驱动

1. 问题描述

目前在手写笔项目中需要使用一块LCD来显示个人信息和当前工作状态等

2. 问题分析

2.1 芯片资料

查看数据说手册中的图,看到如何
在这里插入图片描述
看到这个数据格式为:【地址+控制字+数据字】
从 m 和 n 的格式上看:是支持一次从批量的数据的

  1. 地址需要看SA0的接线方式:
  2. 控制就是读写数据位有效 参考代码为 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));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值