SSD1306 드라이버 탑재 128x64 OLED 모듈

  • 사용 보드 : nRF52840-PDK
  • 사용 IDE : SEGGER Embedded Studio for ARM (노르딕 제품 사용하는 경우 무료 라이센스 이용 가능)
  • 사용 모듈 작동 전압 : 3.3~5V (보드 VDD 사용)
  • 로직 레벨 (SDA, SCK) : GPIO Output high voltage = VDD (3.0V)
  • 통신 방법 : TWI (Two Wire Interface, I2C compatible two-wire interface)
  • Slave Address : 0x3C (사용 모듈 0x3C 고정)
  • SDK 내 예제 프로젝트를 기반으로 작성 (\examples\peripheral\twi_master_using_nrf_twi_mngr)
  • (SDK 다운로드 : https://developer.nordicsemi.com/)

[구동 방식]

 

STM32 - I2C 통신을 이용한 SSD1306 128x64 OLED 제어

작동 전압 : 3.3~5V 통신 방식 : I2C (칩셋이 I2C, SPI를 포함한 5개의 통신 방식을 지원하지만 사용 모듈은 I2C 전용으로 설계됨) 세로 16(Yellow) + 48(Blue) 줄로 구성된 모듈 사용 [기본 구동 방식] Control..

rs29.tistory.com

  • SDK 폴더 내 TWI Transaction Manager Example 를 베이스로 필요에 따라 코드를 수정해 모듈 구동
  • TWI Sensor module 라이브러리를 메인으로 사용
  • (TWI transaction manager 라이브러리에도 데이터 전송 함수들이 존재하지만 사용 방식이 조금 다름,
  • 슬레이브 장치의 레지스터 주소를 입력해 데이터 송수신이 가능한 함수는 TWI Sensor 라이브러리에만 존재)
  • (+ TWI transaction manager 라이브러리는 TWI Sensor module 라이브러리를 사용하기 위해 필요한 라이브러리)

[사전 설정]

- nrf_twi_sensor.c 추가 (SDK 폴더 내 \components\libraries\twi_sensor 에 위치)

- 프로젝트에 twi_sensor 라이브러리 폴더 추가

 

- sdk_config.h 에 TWI Sensor 활성화 부분 추가 및 1로 설정 (nRF_Drivers와 nRF_Libraries 사이에 추가)

(<h>nRF_Drivers_External ~ </h> 까지)

// <e> UART1_ENABLED - Enable UART1 instance
//==========================================================
#ifndef UART1_ENABLED
#define UART1_ENABLED 0
#endif
// </e>

// </e>

// </h> 
//==========================================================

// <h> nRF_Drivers_External 

//==========================================================
// <q> NRF_TWI_SENSOR_ENABLED  - nrf_twi_sensor - nRF TWI Sensor module
 

#ifndef NRF_TWI_SENSOR_ENABLED
#define NRF_TWI_SENSOR_ENABLED 1	//0 비활성화
#endif

// </h> 
//==========================================================

// <h> nRF_Libraries 

//==========================================================
// <e> APP_SCHEDULER_ENABLED - app_scheduler - Events scheduler
//==========================================================
#ifndef APP_SCHEDULER_ENABLED
#define APP_SCHEDULER_ENABLED 1
#endif

- nrf_twi_sensor.h 내 NRF_TWI_SENSOR_SEND_BUF_SIZE 수정 (전송 가능한 최대 버퍼 크기)

/**
 * @brief Internal write operation buffer length.
 *
 * Defines how many bytes can be stored internally.
 * 16 bytes were selected so that nrf_twi_sensor_write_cmd_t size
 * matches nrf_twi_sensor_read_cmd_t size.
 */
#define NRF_TWI_SENSOR_SEND_BUF_SIZE   255  //default 16

 

[코드]

- 정의 및 전역 변수

#define TWI_INSTANCE_ID             0	//TWI instance id used for driver
#define MAX_PENDING_TRANSACTIONS    20	//TWI transaction manager 최대 Queue 개수 (현재 진행 중 제외)

#define SSD1306_ADDR 0x3C		//SSD1306 Slave Address
#define ssd1306_data_select 0x40	//SSD1306 Data 입력할 때 사용
#define ssd1306_cmd_select 0x00		//SSD1306 Command 입력할 때 사용

#define font_width 12	//사용할 폰트의 문자 너비

//Macro that simplifies defining a TWI transaction manager instance.
//생성할 인스턴스 이름, 트랜잭션 큐 크기(최대 보류 트랜잭션 개수), 사용할 하드웨어 TWI 인스턴스 색인
NRF_TWI_MNGR_DEF(m_nrf_twi_mngr, MAX_PENDING_TRANSACTIONS, TWI_INSTANCE_ID);

//Macro creating common twi sensor instance.
//TWI 공통 센서 인스턴스 이름, TWI Manager 인스턴스 포인터, 통신에 사용될 버퍼의 크기
NRF_TWI_SENSOR_DEF(twi_ssd1306, &m_nrf_twi_mngr, MAX_PENDING_TRANSACTIONS);
  • TWI Sensor module 라이브러리 사용은 TWI transaction manager 라이브러리 사용을 전제로 함
  • MAX_PENDING_TRANSACTIONS은 트랜잭션 매니저 큐에 최대 보류 가능한 트랜잭션 개수
  • NRF_TWI_SENSOR_DEF 에서 MAX_PENDING_TRANSACTIONS 은 TWI manager 큐 크기보다 작거나 같아야 함
  • 데이터 송수신을 실행할 경우 트랜잭션 매니저 큐에 실행 순서대로 스케줄링 된 뒤 통신이 이루어진다

- TWI 설정

static void twi_config(void)
{
    uint32_t err_code;
    
    nrf_drv_twi_config_t const config={
      .scl=27,	//SCL PIN
      .sda=26,	//SDA PIN
      .frequency=NRF_DRV_TWI_FREQ_400K,		//TWI Frequency = 400KHz
      .interrupt_priority=APP_IRQ_PRIORITY_MID	//Interrupt Priority
      };
    
    err_code=nrf_twi_mngr_init(&m_nrf_twi_mngr,&config);
    APP_ERROR_CHECK(err_code);
}
  • TWI 통신 환경 설정
  • 통신에 사용할 핀, 속도, 인터럽트 우선 순위 등을 설정한 뒤 이 환경 설정을 기반으로 TWI 트랜잭션 매니저 인스턴스 초기화

- SSD1306 Command 입력 함수

void ssd1306_write_cmd(uint8_t ssd1306_cmd)
{
    //센서 인스턴스 포인터, 슬레이브 장치 주소, 레지스터 주소(슬레이브 내), 전송할 데이터 포인터, 전송할 바이트 크기
    nrf_twi_sensor_reg_write(&twi_ssd1306, SSD1306_ADDR, ssd1306_cmd_select, &ssd1306_cmd, 1);
}
  • TWI 트랜잭션 매니저 라이브러리 내에도 데이터 전송 함수가 존재하지만 슬레이브 장치내 특정 레지스터 주소에 접근해서 데이터 송수신이 가능한 함수는 TWI 센서 모듈 라이브러리에만 존재한다
  • (SSD1306 사용의 경우, TWI sensor 라이브러리 전송 함수의 레지스터 주소 부분을 이용해 사용자가 전송할 데이터 버퍼가 명령어인지 아니면 디스플레이 데이터인지를 결정하는데 사용)
  • 예) 명령어 0xA8을 전송하는 경우,

슬레이브 장치 주소, 컨트롤 바이트(커맨드 설정), 0xA8 (커맨드)

- SSD1306 초기화 함수

static void ssd1306_init(void)
{
	ssd1306_W_Command(0xA8);	//Set Mux Ratio
	ssd1306_W_Command(0x3F);	//64MUX

	ssd1306_W_Command(0xD3);	//Set Display Offset
	ssd1306_W_Command(0x00);	//COM0

	ssd1306_W_Command(0x40);	//Set Display Start Line

	ssd1306_W_Command(0xA1);	//Set Segment re-map, Default 0xA0
					//column address 127 is mapped to SEG0 (좌우 반전)

	ssd1306_W_Command(0xC8);	//Set COM Output Scan Direction, default 0xC0
					//remapped mode. Scan from COM[N-1] to COM0 (상하 반전)

	ssd1306_W_Command(0xDA);	//Set COM Pins hardware configuration
	ssd1306_W_Command(0x12);

	ssd1306_W_Command(0x20);	//Set Memory Addressing Mode
	ssd1306_W_Command(0x02);	//Page Addressing Mode

	ssd1306_W_Command(0x81);	//Set Contrast Control
	ssd1306_W_Command(0x7F);	//1~256

	ssd1306_W_Command(0xA4);	//Disable Entire Display On

	ssd1306_W_Command(0xA6);	//Set Normal Display

	ssd1306_W_Command(0xD5);	//Set Osc Frequency
	ssd1306_W_Command(0x80);

	ssd1306_W_Command(0x8D);	//Enable charge pump regulator
	ssd1306_W_Command(0x14);

	ssd1306_W_Command(0xAF);	//Display ON
}
  • 데이터 시트에 나와있는 초기화 예를 기반으로 작성
  • 페이지 어드레싱 모드 사용
  • 사용 모듈의 경우, 초기화 코드를 그대로 사용하면 노란색 영역이 하단에 위치 (Page 6,7)
  • 노란색 영역을 상단으로 사용하기 위해 0xC8 명령어를 사용해 COM 출력 스캔 방향을 COM63 to COM0 으로 바꿔 상하 반전을 시키고 0xA1 명령어를 통해 127 열을 SEG0 으로 리맵핑 하여 좌우를 반전 시켜줌
  • Set COM Pins hardware configuration의 경우, COM0~COM63이 순차적으로 화면 최상단에서 최하단까지 이어지는 Sequential 핀 배열과 COM0~COM31, COM32~COM63 이 교차되어 나열되는 (COM0, COM31, COM2... / COM31, COM0, COM32...) Alternative 핀 배열의 두 방법이 있다
  • Sequential 배열을 사용하기 위해 데이터 시트를 참고해 0xDA+0x02 명령어를 사용했지만 실제 결과는 Alternative 배열처럼 COM0~COM31/COM32~COM63이 서로 교차되어 표시되는 결과를 얻음
  • 0xDA+0x12(Alternative COM 핀 배열) 명령어를 사용한 결과, COM0~COM63 까지 화면에 순차적으로 표시됨

- SSD1306 데이터 전송 함수

static void ssd1306_write_data(uint8_t* data_buffer, int buf_length)
{
    //센서 인스턴스 포인터, 센서 주소, 레지스터 주소, 전송할 데이터 버퍼 주소, 버퍼 길이
    nrf_twi_sensor_reg_write(&twi_ssd1306,SSD1306_ADDR,ssd1306_data_select,&data_buffer[0],buf_length);
}
  • 디스플레이에 표시될 데이터를 전송하는 함수 (GDDRAM에 저장될 데이터)
  • ssd1306_data_select의 값은 0x40 으로 센서에 전송되는 첫번째 바이트이며 뒤이어 전송될 데이터 버퍼가 디스플레이 표현에 사용될 데이터임을 나타낸다
  • 전송 데이터 버퍼는 내부 버퍼로 복사된다 (nRF52 내)
  • 버퍼 길이의 경우, nrf_twi_sensor.h 내 NRF_TWI_SENSOR_SEND_BUF_SIZE 의 값에 -1된 값이 전송 가능한 최대 길이가 된다. 따라서 전송할 버퍼의 길이는 NRF_TWI_SENSOR_SEND_BUF_SIZE 값보다 작아야 한다

- SSD1306 디스플레이 클리어 함수

static void ssd1306_clear(void)
{
    static uint8_t clear_buffer[128]={0};	//GDDRAM에 전송할 데이터 버퍼

    //데이터 입력이 시작될 열 주소를 0번째 열로 설정
    ssd1306_write_cmd(0x00);
    ssd1306_write_cmd(0x10);

    for(int i=0;i<8;i++)
    {
        ssd1306_write_cmd(0xB0+i);	//데이터를 입력할 페이지 설정
        ssd1306_write_data(clear_buffer,sizeof(clear_buffer)/sizeof(uint8_t));	//데이터 버퍼 전송
    }
}
  • 디스플레이를 빈 화면으로 초기화하는 함수
  • 0x00, 0x10 명령어를 먼저 전송해 시작 열을 0으로 설정
  • FOR문 - 0xB0 명령어를 입력해 시작 페이지를 0번째 페이지로 설정
  • 0x00 값을 갖는 128 개의 데이버 버퍼를 데이터 전송 함수를 통해 SSD1306에 전송
  • 전송 완료 후, 열의 위치는 0번째 열이 된다
  • (열에 데이터를 입력하고 나면 열 주소가 자동으로 1 증가되는데 마지막 127열의 경우에는 데이터를 입력하고 난 뒤에 첫번째 열로 주소가 변경됨)
  • 7번째 페이지까지 반복해 GDDRAM에 저장된 디스플레이 데이터를 전부 0으로 설정해 화면을 비움

- SSD1306 화면 좌표 설정 함수

static void ssd1306_Set_Coord(uint8_t page, uint8_t col)
{
    uint8_t col_low=0x0F,col_high=0x1F;
    col_low=(col&0x0F);			//열 주소 하위 4비트 계산
    col_high=0x10|((col>>4)&0x0F);	//열 주소 상위 4비트 계산
    ssd1306_write_cmd(0xB0+page);	//페이지 주소 전송
    ssd1306_write_cmd(col_low);		//열 주소 하위 4비트 전송
    ssd1306_write_cmd(col_high);	//열 주소 상위 4비트 전송
}
  • 디스플레이 데이터를 쓰기 시작할 위치를 지정하는 함수
  • Page Addressing Mode의 경우 Horizontal, Vertical 모드와는 달리 페이지 및 열의 마지막 입력이 필요치 않으므로 시작 지점만을 입력한다
  • 페이지 시작 주소 설정은 0xB0 + 원하는 페이지의 값(0~7)을 더해 전송하면 된다
  • 열 시작 주소는 전달받은 열을 상위 4bit, 하위 4bit로 나누어 하위 4bit는 0x0F, 상위 4bit는 LSB 방향으로 4비트 시프트 한 뒤 0x1F와 AND 연산한 결과값을 전송
  • ex) 100번째 열을 시작 주소로 설정할 때 100 = 0x64
  • 하위 4비트 : 0x64를 0x0F와 AND 연산해 하위 4비트 주소를 만듦 (0x64 & 0x0F = 0x04)
  • 상위 4비트 : 0x64를 LSB 방향으로 4비트 시프트 한 뒤, 0x0F와 AND 연산해 4비트 주소값을 구하고 이 값에 0x10을 더해 상위 4비트 주소를 만듦

[폰트]

 

The Dot Factory: An LCD Font and Image Generator

The Dot Factory is a small open source tool (MIT licensed) intended to generate the required C language information to store many fonts and images, as efficiently as possible, on a microcontroller. These fonts are then uploaded via the LCD driver (see the

www.eran.io

  • 링크의 프로그램을 이용해 폰트 배열을 생성한 뒤 사용
  • 시스템 내에 설치된 폰트를 바탕으로 입력된 문자(특문,대문자,소문자) 및 폰트 크기 설정에 따라 자동으로 문자 배열을 생성해줌
  • Sequential 핀 배열을 설정했으므로 연속된 두 페이지(0,1/2,3/...)에 폰트를 상하로 1Byte씩 나눠서 저장함
  • 두 페이지를 한 줄 기준으로 삼았기 때문에 변환된 폰트의 높이가 16bit 이하여야 한다
  • 예) 12x16 bit 크기로 변환된 A를 지정된 좌표에 입력
  • (같은 폰트 내에서도 각 글자마다 크기가 서로 다르므로 12x16 bit 내에서 차지하는 높이나 너비가 다르다)

/* @792 'A' (12 pixels wide) */
//
//      #
//     # #
//     # #
//     # #
//    #   #
//    #   #
//    #####
//   #     #
//   #     #
//   #     #
//  #       #
//
//
//
//
0x00, 0x00, 0x00, 0xE0, 0x9C, 0x82, 0x9C, 0xE0, 0x00, 0x00, 0x00, 0x00, //상단 페이지에 저장
0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, //하단 페이지에 저장

 

- SSD1306 문자 전송 함수

//문자와 입력 주소를 전달받아 디스플레이에 표현하는 함수
static void ssd1306_W_Char(uint8_t character_Code, uint8_t page, uint16_t column)
{
    uint8_t char_Buffer[font_width*2]={0};

    for(uint8_t i=0;i<font_width*2;i++)
    {
        char_Buffer[i]=ssd1306_Fonts[(character_Code-32)*(font_width*2)+i];
    }

    for(uint8_t i=0;i<2;i++)
    {
        ssd1306_Set_Coord(page+i,column);
        ssd1306_write_data(&char_Buffer[i*font_width],font_width);
    }
}
  1. 변환된 폰트의 너비*2만큼의 원소 개수를 갖는 배열 선언 (*2는 글자를 상/하 페이지로 나누어 저장하기 위함)
  2. 입력된 문자를 int 변수로 받아 ASCII 코드값을 기반으로 저장한 폰트 배열에서 매칭되는 문자를 찾는다
    • 폰트 배열의 경우 아스키 코드 32번 space부터 시작하여 126번 ~ 까지 순서대로 변환, 저장해서 사용했다
    • space의 아스키 코드값(32)과 폰트 배열 0번의 차인 32가 폰트 배열에서 입력된 문자를 찾는 기준점이 된다
    • 하나의 문자에서 다음 문자로 넘어가는데는 문자 너비*2 만큼의 수가 필요
    • (전달된 문자의 아스키 코드값-32)*(폰트 너비*2)를 하면 문자와 매칭되는 폰트 배열의 시작점이 나온다
  3. 해당 문자가 저장된 폰트 배열 시작점에서부터 순서대로 앞서 생성한 char_Buffer 배열에 폰트 데이터를 저장한다
  4. 페이지 및 열 포인터 시작 주소를 지정하고 우선 폰트 너비만큼의 데이터를 전송해 폰트의 상단 부분을 표시
  5. 페이지 주소 1 증가 및 열 시작 주소를 재지정한 뒤 남은 배열을 전송해 폰트 하단 부분을 완성

왼쪽 : 폰트 상단 부분 (0페이지), 오른쪽 : 폰트 상단 (0 페이지) + 하단 (1 페이지)

- SSD1306 문자열 전송 함수

//문자열과 입력 주소를 전달받아 디스플레이에 표현하는 함수
static void ssd1306_W_String(char *str, uint8_t page, uint8_t col)
{
    while(*str)
    {
        //문자가 입력될 열 주소에 폰트 너비를 더한 값이 열의 최대 값인 127을 넘는 경우
        //제대로 된 문자 표현이 불가능하므로 다음 페이지로 넘기거나 or 마지막 페이지일 경우, 전송 중단
        if((127<col+font_width))	
        {
            if(page==6)
            {
                    break;	//마지막 페이지일 경우, 전송 중단
            }
            page+=2;	//다음 페이지에 이어서 문자 입력
            col=0;	//첫번째 열부터 문자 입력 시작
        }
        ssd1306_W_Char(*str,page,col);	//문자 전송 함수에 문자 및 문자 입력 주소 전달

        col+=font_width;	//폰트 너비만큼 열 증가
        str++;			//문자열 주소 +1 (다음 문자로)
    }
}
  • NULL 값이 나올 때까지 전달받은 문자열 주소 1씩 증가시키며 while 문 반복
  • 문자 전송 함수를 사용해 입력 주소에 첫번째 문자부터 순차적으로 전송
  • 문자 입력 위치에 문자열을 입력할 공간이 부족하다면 다음 페이지로 넘어가 첫번째 열부터 문자 입력 시작
  • 마지막 페이지일 경우, 더 이상 문자를 입력할 공간이 없다면 전송 중단

- 전체 코드 및 간단한 반복 구동 예시 (twi_master_using_nrf_twi_mngr 예제 프로젝트 기반)

/**
 * Copyright (c) 2015 - 2019, Nordic Semiconductor ASA
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, must reproduce the above copyright notice, this list of
 *    conditions and the following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
/** @file
 * @defgroup nrf_twi_master_example main.c
 * @{
 * @ingroup nrf_twi_example
 * @brief TWI Example Application main file.
 *
 * This file contains the source code for a sample application using TWI.
 */

#include "boards.h"
#include "app_util_platform.h"
#include "nrf_drv_clock.h"
#include "bsp.h"
#include "app_error.h"
#include "nrf_twi_mngr.h"

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

#include "nrf_twi_sensor.h"
#include "fonts.h"
#include "nrf_delay.h"

#define TWI_INSTANCE_ID             0
#define MAX_PENDING_TRANSACTIONS    20

#define SSD1306_ADDR 0x3C
#define ssd1306_data_select 0x40
#define ssd1306_cmd_select 0x00

#define font_width 12

static uint8_t ssd1306_w_buffer[2]={0};
static uint8_t ssd1306_display_buf[128]={0};

//Macro that simplifies defining a TWI transaction manager instance.
NRF_TWI_MNGR_DEF(m_nrf_twi_mngr, MAX_PENDING_TRANSACTIONS, TWI_INSTANCE_ID);

//Macro creating common twi sensor instance.
NRF_TWI_SENSOR_DEF(twi_ssd1306,&m_nrf_twi_mngr,MAX_PENDING_TRANSACTIONS);

void log_init(void)
{
    ret_code_t err_code;

    err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);

    NRF_LOG_DEFAULT_BACKENDS_INIT();
}

static void twi_config(void)
{
    uint32_t err_code;
    
    nrf_drv_twi_config_t const config={
      .scl=27,
      .sda=26,
      .frequency=NRF_DRV_TWI_FREQ_400K,
      .interrupt_priority=APP_IRQ_PRIORITY_MID
      };
    
    err_code=nrf_twi_mngr_init(&m_nrf_twi_mngr,&config);
    APP_ERROR_CHECK(err_code);
}

void ssd1306_write_cmd(uint8_t ssd1306_cmd)
{    
    nrf_twi_sensor_reg_write(&twi_ssd1306,SSD1306_ADDR,ssd1306_cmd_select,&ssd1306_cmd,1);
}

static void ssd1306_init(void)
{
    ssd1306_write_cmd(0xA8);
    ssd1306_write_cmd(0x3F);

    ssd1306_write_cmd(0xD3);
    ssd1306_write_cmd(0x00);

    ssd1306_write_cmd(0x40);

    ssd1306_write_cmd(0xA1);

    ssd1306_write_cmd(0xC8);

    ssd1306_write_cmd(0xDA);
    ssd1306_write_cmd(0x12);

    ssd1306_write_cmd(0x20);
    ssd1306_write_cmd(0x02);

    ssd1306_write_cmd(0x81);
    ssd1306_write_cmd(0x7F);

    ssd1306_write_cmd(0xA4);

    ssd1306_write_cmd(0xA6);

    ssd1306_write_cmd(0xD5);
    ssd1306_write_cmd(0x80);

    ssd1306_write_cmd(0x8D);
    ssd1306_write_cmd(0x14);

    ssd1306_write_cmd(0xAF);
}


void ssd1306_write_data(uint8_t* data_buffer, int buf_length)
{
    nrf_twi_sensor_reg_write(&twi_ssd1306,SSD1306_ADDR,ssd1306_data_select,&data_buffer[0],buf_length);
}

void ssd1306_clear(void)
{
    static uint8_t clear_buffer[128]={0};

    ssd1306_write_cmd(0x00);
    ssd1306_write_cmd(0x10);

    for(int i=0;i<8;i++)
    {
        ssd1306_write_cmd(0xB0+i);
        ssd1306_write_data(clear_buffer,sizeof(clear_buffer)/sizeof(uint8_t));
    }
}

static void ssd1306_fill(void)
{
    static uint8_t fill_buffer[128];

    for(int i=0;i<128;i++)
    {
        fill_buffer[i]=0xff;
    }

    ssd1306_write_cmd(0x00);
    ssd1306_write_cmd(0x10);

    for(int i=0;i<8;i++)
    {
        ssd1306_write_cmd(0xB0+i);
        ssd1306_write_data(fill_buffer,sizeof(fill_buffer)/sizeof(uint8_t));
    }
}

static void ssd1306_Set_Coord(uint8_t page, uint8_t col)
{
    uint8_t col_low=0x0F,col_high=0x1F;
    col_low=(col&0x0F);
    col_high=0x10|((col>>4)&0x0F);
    ssd1306_write_cmd(0xB0+page);
    ssd1306_write_cmd(col_low);
    ssd1306_write_cmd(col_high);
}

static void ssd1306_W_Char(uint8_t character_Code, uint8_t page, uint16_t column)
{
    uint8_t char_Buffer[font_width*2]={0};

    for(uint8_t i=0;i<font_width*2;i++)
    {
        char_Buffer[i]=ssd1306_Fonts[(character_Code-32)*(font_width*2)+i];
    }

    for(uint8_t i=0;i<2;i++)
    {
        ssd1306_Set_Coord(page+i,column);
        ssd1306_write_data(&char_Buffer[i*font_width],font_width);
    }
}

static void ssd1306_W_String(char *str, uint8_t page, uint8_t col)
{
    while(*str)
    {
        if((127<col+font_width))
        {
            if(page==6)
            {
                    break;
            }
            page+=2;
            col=0;
        }
        ssd1306_W_Char(*str,page,col);

        col+=font_width;
        str++;
    }
}

int main(void)
{
    ret_code_t err_code;
    
    log_init();

    NRF_LOG_RAW_INFO("\r\nTWI master example started. \r\n");
    NRF_LOG_FLUSH();
    
    twi_config();
    err_code=nrf_twi_sensor_init(&twi_ssd1306);
    APP_ERROR_CHECK(err_code);
    
    ssd1306_init();
    
    char number_str[48];
    sprintf(number_str,"0123456789!@#$%^&*()0123456789!@#$%^&*()012345",sizeof(number_str));

    while (true)
    {
        nrf_delay_ms(2000);
        ssd1306_clear();
        nrf_delay_ms(2000);
        ssd1306_W_String("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz{|}~",0,0);
        nrf_delay_ms(2000);
        ssd1306_W_String(number_str,0,0);
        nrf_delay_ms(2000);
        ssd1306_fill();
        
        NRF_LOG_FLUSH();
    }
}
/** @} */
  1. SSD1306 초기화
  2. 48개의 배열을 갖는 char 변수 선언 및 sprintf 함수를 사용해 숫자, 특수문자를 입력 (폰트 너비 12, 문자 4줄 입력)
  3. 디스플레이 빈 화면으로 초기화
  4. 문자열 입력 (첫번째 페이지, 0번째 열부터 마지막 페이지까지 순서대로 표현 가능한 부분까지만 입력)
  5. 앞서 선언 및 문자열을 입력한 char 변수를 SSD1306에 전송 (동일하게 첫 페이지, 0번째 열부터 마지막 페이지까지 표현 가능한 부분만 입력)
  6. 0xFF 배열 128개를 0~7페이지까지 전송해 디스플레이의 모든 LED를 켬
  7. 다시 3번으로

 

 

+ Recent posts