2014年5月19日月曜日

BeaglBone BlackにMARY OLDEボードを接続(SPIを使う)

トラ技のMARYのOLEDボード(128x128のカラーLCD)をBeableBone Blackにつなげる。


OLEDはUG-2828GDEDF11。
これはSPIで制御される。

制御方法は
http://mbed.org/users/kanpapa/notebook/mary-oled-board/
が参考になる。
データシートは
http://www.adafruit.com/datasheets/UG-2828GDEDF11.pdf
でもSSD1351で検索して見つかったデータシートが一番いい。

また、加速度センサ
LIS33DE
がのっているらしい。
これはI2C。アドレスは0x1C。

OLEDはSPIのほかにresetとVCC_ONを制御する必要があるが、
resetはP8_14、VCC_ONはP8_12を使うことにする。

beaglebone blackでspiを有効にするには
http://www.nagavenkat.adurthi.com/2014/02/spi-communication-beaglebone-black-as-master-to-arduino-as-slave/

http://elinux.org/BeagleBone_Black_Enable_SPIDEV

SPI0はI2C0とぶつかるので、
HDMIを殺して、SPI1を使うことにする。

/boot/uboot/uEnv.txtを編集して
optargs=quiet drm.debug=7 capemgr.disable_partno=BB-BONELT-HDMI,BB-BONELT-HDMIN
capemgr.enable_partno=BB-SPIDEV1
を追加する。
なお、ubuntu13.10には/lib/firmwareに
BB-SPIDEV0-00A0.dtdo、BB-SPIDEV1-00A0.dtdoがあったので、今回はSPIDEV1を使った。
念のためにcat /sys/kernel/debug/pinctrl/44e10800.pinmux/pinsを確認すると
pin 100 (44e10990) 00000033 pinctrl-single 
pin 101 (44e10994) 00000033 pinctrl-single 
pin 102 (44e10998) 00000013 pinctrl-single 

pin 103 (44e1099c) 00000013 pinctrl-single
なので、
pin 100 -> P9_31 : SPI1_SCLK -> input
pin 101 -> P9_29 : SPI1_D0 -> input = MISO
pin 102 -> P9_30 : SPI1_D1 -> output = MOSI
pin 103 -> P9_28 : SPI1_CS0 -> output

spidevの利用例は
http://manual.atmark-techno.com/armadillo-guide/armadillo-guide-3_ja-2.0.0/ch02.html#sec_using_spi_with_adc
を参考にする。

OLEDの初期化はSDD1351のデータシートに説明があるが、
mbed向けのサンプルが参考になる。
<https://mbed.org/users/nxpfan/notebook/MARMEX_OB_oled_lib/>
<https://mbed.org/users/nxpfan/code/MARMEX_OB_oled__HelloWorld/>
制御そのものも参考になるし、文字データもある。

[oledspi.cpp]
/*
 * oledspi.cpp
 *
 *  Created on: 2014/05/20
 *      Author: yamamoto
 */

#include "oledspi.h"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include <math.h>
#include <signal.h>
#include <string.h>

#include "font.h"

oled_spi::oled_spi(const char* a_pcharDevice, unsigned char a_u8Mode,
unsigned char a_u8Bits, unsigned long a_u32Speed,
unsigned short a_u16Delay, unsigned char a_u8CsCnage) :
mc_u8SpiMode(a_u8Mode), mc_u8SpiBitsWrite(a_u8Bits), mc_u8SpiBitsRead(
8), mc_u32SpiSpeedHz(a_u32Speed), mc_u16SpiDelay(a_u16Delay), mc_u8CsChange(
a_u8CsCnage), m_gpioReset(26), m_gpioVccOn(44), m_iColorForeground(
0xFFFFFF), m_iColorBackground(0), m_u8TextPositionColumn(0), m_u8TextPositionRow(
0) {
/// open device file
if (0 > (m_iDeviceHandle = open(a_pcharDevice, O_RDWR))) {
fprintf(stderr, "error: device open, %s\n", a_pcharDevice);
exit(1);
}

/// reset oled
reset();

/// initialize oled
initialize();
}

oled_spi::~oled_spi() {
/// shutdown oled
shutdown_oled();

/// close device file
close(m_iDeviceHandle);
}

void oled_spi::reset() {
printf("oled reset\n");
m_gpioReset.set(0);

// wait 2us
usleep(2);

m_gpioReset.set(1);

// VCC_OFF
m_gpioVccOn.set(0);
}

void oled_spi::initialize() {
printf("oled initialize\n");

init_spi();

init_oled();

clear();
}

void oled_spi::init_spi() {
// mode
if (0 > ioctl(m_iDeviceHandle, SPI_IOC_WR_MODE, &mc_u8SpiMode)) {
fprintf(stderr, "error: spi set write mode\n");
exit(1);
}
if (0 > ioctl(m_iDeviceHandle, SPI_IOC_RD_MODE, &mc_u8SpiMode)) {
fprintf(stderr, "error: spi set read mode\n");
exit(1);
}
// bit length
if (0
> ioctl(m_iDeviceHandle, SPI_IOC_WR_BITS_PER_WORD,
&mc_u8SpiBitsWrite)) {
fprintf(stderr, "error: spi set write bit length\n");
exit(1);
}
if (0
> ioctl(m_iDeviceHandle, SPI_IOC_RD_BITS_PER_WORD,
&mc_u8SpiBitsRead)) {
fprintf(stderr, "error: spi set read bit length\n");
exit(1);
}
// max speed Hz
if (0
> ioctl(m_iDeviceHandle, SPI_IOC_WR_MAX_SPEED_HZ,
&mc_u32SpiSpeedHz)) {
fprintf(stderr, "error: spi set write speed\n");
exit(1);
}
if (0
> ioctl(m_iDeviceHandle, SPI_IOC_RD_MAX_SPEED_HZ,
&mc_u32SpiSpeedHz)) {
fprintf(stderr, "error: spi set read speed\n");
exit(1);
}
}

void oled_spi::init_oled() {
#define GAMMA_LUT_SIZE 63
unsigned char gamma_LUT[GAMMA_LUT_SIZE];

for (int i = 0; i < GAMMA_LUT_SIZE; i++)
gamma_LUT[i] = (unsigned char) (powf(((float) i / 62.0), (1.0 / 0.58))
* 178.0 + 2.0);

sendCommand(SET_DISPLAY_MODE_ALL_OFF);
sendCommand(SET_COMMAND_LOCK);
sendData(0x12);

sendCommand(SET_COMMAND_LOCK);
sendData(0xb1);

sendCommand(SET_SLEEP_MODE_ON);

sendCommand(FRONT_CLOCK_DRIVER_OSC_FREQ);
sendData(0xF1);

sendCommand(SET_MUX_RATIO);
sendData(0x7F);

sendCommand(SET_DISPAY_OFFSET);
sendData(0x00);

sendCommand(SET_DISPAY_START_LINE);
sendData(0x00);

sendCommand(SET_REMAP_COLOR_DEPTH);
sendData(0x74);

sendCommand(SET_GPIO);
sendData(0x00);

sendCommand(FUNCTION_SELECTION);
sendData(0x01);

sendCommand(SET_SEGMENT_LOW_VOLTAGE);
sendData(0xA0);
sendData(0xB5);
sendData(0x55);

sendCommand(SET_CONTRAST_CURRENT_FOR_COLOR_ABC);
sendData(0xC8);
sendData(0x80);
sendData(0xC8);

sendCommand(MASTER_CONTRAST_CURRENT_CONTROL);
sendData(0x0F);

sendCommand(LOOKUP_TABLE_FOR_GRAYSCALE_PULSE_WIDTH);
for (int i = 0; i < GAMMA_LUT_SIZE; i++)
sendData(gamma_LUT[i]);

sendCommand(SET_RESET_PRECHARGE_PERIOD);
sendData(0x32);

sendCommand(ENHANCE_DRIVING_SCHEME_CAPABILITY);
sendData(0x04);
sendData(0x00);
sendData(0x00);

sendCommand(SET_PRECHARGE_VOLTAGE);
sendData(0x17);

sendCommand(SET_SECOND_PRECHARGE_VOLTAGE);
sendData(0x01);

sendCommand(SET_VCOMH_VOLTAGE);
sendData(0x05);

sendCommand(SET_DISPLAY_MODE_RESET);

#if 0
sendCommand( SET_COLUMN_ADDRESS );
sendData( 0x00 );
sendData( 0x7F );

sendCommand( SET_ROW_ADDRESS );
sendData( 0x00 );
sendData( 0x7F);

sendCommand( WRITE_RAM_COMMAND );
for ( int i = 0; i < WIDTH * HEIGHT; i++ )
sendData( 0x00 );
#endif

// TODO cls();

// VCC_ON
m_gpioVccOn.set(1);

// wait 100msec
usleep(100 * 1000);

sendCommand(SET_SLEEP_MODE_OFF);
}

void oled_spi::sendCommand(unsigned char a_u8Command) {
if (1 > send(0x00FF & (unsigned short) a_u8Command)) {
fprintf(stderr, "error: send command, 0x%2.0X\n", a_u8Command);
}
}

void oled_spi::sendData(unsigned char a_u8Data) {
if (1 > send(0x0100 | (unsigned short) a_u8Data)) {
fprintf(stderr, "error: send Data, 0x%2.0X\n", a_u8Data);
}
}

void oled_spi::shutdown_oled() {
printf("oled shutdown\n");

// send display off
sendCommand(SET_SLEEP_MODE_ON);

// VCC_OFF
m_gpioVccOn.set(0);
}

void oled_spi::clear() {
  fillRect(0, 0, msc_u8OledWidth, msc_u8OledHeight, 0);
  m_u8TextPositionColumn = 0;
  m_u8TextPositionRow = 0;
}

void oled_spi::drawPixel(unsigned char x, unsigned char y, int rgb888) {
setWindow(x, y, 1, 1);
setPixel(rgb888);
}

void oled_spi::drawLine(unsigned char x1, unsigned char y1, unsigned char x2,
unsigned char y2, int rgb888) {
// TODO
}

void oled_spi::drawRect(unsigned char x1, unsigned char y1, unsigned char x2,
unsigned char y2, int rgb888) {
drawLine(x1, y1, x1, y2, rgb888);
drawLine(x1, y1, x2, y1, rgb888);
drawLine(x2, y1, x2, y1, rgb888);
drawLine(x1, y2, x2, y2, rgb888);
}

void oled_spi::drawImage(unsigned char x, unsigned char y, unsigned char width,
unsigned char height, int* pRgb888) {
setWindow(x, y, width, height);
for (int i = 0; i < width * height; i++) {
setPixel(pRgb888[i]);
}
}

void oled_spi::drawBitmap(unsigned char x, unsigned char y, unsigned char width,
unsigned char height, const unsigned char* bitmap) {
setWindow(x, y, width, height);
for (int i = 0; i < height * width; i++) {
int byte = i / 8;
int bit = i % 8;
int color =
((bitmap[byte] << bit) & 0x80) ?
m_iColorForeground : m_iColorBackground;
setPixel(color);
}
}

void oled_spi::setColor(int foreground, int background) {
setColorForeground(foreground);
setColorBackground(background);
}

void oled_spi::setColorForeground(int rgb888) {
m_iColorForeground = rgb888;
}

void oled_spi::setColorBackground(int rgb888) {
m_iColorBackground = rgb888;

}

void oled_spi::drawText(unsigned char column, unsigned char row,
unsigned char length, char* text) {
for (unsigned char i = column;
i < (column + length) && i < msc_u8TextColumnNum; i++) {
drawChar(i, row, text[i - column]);
}
}

void oled_spi::drawChar(unsigned char column, unsigned char row, char text) {
unsigned char x = column * (msc_u8TextWidth + 1);
unsigned char y = row * (msc_u8TextHeight + 1);
drawBitmap(x, y, msc_u8TextWidth, msc_u8TextHeight,
  font::FONT8x8[text - 0x20]);
}

int oled_spi::send(unsigned short a_u16Buf) {
spi_ioc_transfer trans = { 0 };
unsigned short u16Read;
trans.tx_buf = (unsigned long) &a_u16Buf;
trans.rx_buf = (unsigned long) NULL;
trans.len = 2;
trans.delay_usecs = mc_u16SpiDelay;
trans.speed_hz = mc_u32SpiSpeedHz;
trans.bits_per_word = mc_u8SpiBitsWrite;
trans.cs_change = mc_u8CsChange;

int ret = ioctl(m_iDeviceHandle, SPI_IOC_MESSAGE(1), &trans);
if (1 > ret) {
printf("error : send : %4.0X\n", a_u16Buf);
}

return ret;
}

void oled_spi::setWindow(unsigned char x, unsigned char y, unsigned char width,
unsigned char height) {
unsigned char x1 = x;
unsigned char x2 = (unsigned char) (x + width - 1);
unsigned char y1 = y;
unsigned char y2 = (unsigned char) (y + height - 1);

sendCommand(SET_COLUMN_ADDRESS);
sendData(x1);
sendData(x2);
sendCommand(SET_ROW_ADDRESS);
sendData(y1);
sendData(y2);
sendCommand(WRITE_RAM_COMMAND);
}

void oled_spi::setPixel(int rgb888) {
unsigned short rgb565 = 0;
rgb565 = (unsigned short) ((rgb888 >> 8) & 0xf800);
rgb565 |= (unsigned short) ((rgb888 >> 5) & 0x07e0);
rgb565 |= (unsigned short) ((rgb888 >> 3) & 0x001f);

sendData((unsigned char) (rgb565 >> 8));
sendData((unsigned char) rgb565);
}

void oled_spi::fillRect(unsigned char x, unsigned char y, unsigned char width,
unsigned char height, int rgb888) {
setWindow(x, y, width, height);
for (int i = 0; i < width * height; i++) {
setPixel(rgb888);
}
}

int main(int argc, char* argv[]) {

oled_spi oled("/dev/spidev1.0", 0, 9, 16 * 1000 * 1000 /* 16MHz */, 0, 0);

// TODO for debug
char text[] = "Hello World!";
oled.drawText(0, 0, strlen(text), text);
sleep(5);

for (int i = 0; i < 5; i++) {
oled.fillRect(0, 0, 128, 128, 0xFF0000);
//usleep(100000);
oled.fillRect(0, 0, 128, 128, 0x00FF00);
//usleep(100000);
oled.fillRect(0, 0, 128, 128, 0x0000FF);
//usleep(100000);
}

oled.clear();
oled.fillRect(0, 0, 128, 128, 0x505050);
oled.setColorForeground(0xFF0000);
oled.drawText(0, 0, strlen(text), text);
sleep(10);
return 0;

}

[oledspi.h]
/*
 * oledspi.h
 *
 *  Created on: 2014/05/20
 *      Author: yamamoto
 */

#ifndef OLEDSPI_H_
#define OLEDSPI_H_

#include <gpio.h>
/*
 *
 */
class oled_spi {
public:
oled_spi(const char* a_pcharDevice, unsigned char a_u8Mode,
unsigned char a_u8Bits, unsigned long a_u32Speed,
unsigned short a_u16Delay, unsigned char a_u8CsCnage);
virtual ~oled_spi();

void setColor(int foreground, int background);
void setColorForeground(int rgb888);
void setColorBackground(int rgb888);
void clear();
void fillRect(unsigned char x, unsigned char y, unsigned char width,
unsigned char height, int rgb888);
void drawPixel(unsigned char x, unsigned char y, int rgb888);
void drawLine(unsigned char x1, unsigned char y1, unsigned char x2,
unsigned char y2, int rgb888);
void drawRect(unsigned char x1, unsigned char y1, unsigned char x2,
unsigned char y2, int rgb888);
void drawImage(unsigned char x, unsigned char y, unsigned char width,
unsigned char height, int* pRgb888);
void drawBitmap(unsigned char x, unsigned char y, unsigned char width,
unsigned char height, const unsigned char* bitmap);
void drawText(unsigned char column, unsigned char row, unsigned char length,
char* text);
void drawChar(unsigned char column, unsigned char row, char text);

protected:
void reset();
void initialize();

void setWindow(unsigned char x, unsigned char y, unsigned char width,
unsigned char height);
void setPixel(int rgb888);

private:
int m_iDeviceHandle;

static const unsigned char msc_u8OledWidth = 128;
static const unsigned char msc_u8OledHeight = 128;
static const unsigned char msc_u8TextWidth = 8;
static const unsigned char msc_u8TextHeight = 8;
static const unsigned char msc_u8TextColumnNum = msc_u8OledWidth
/ (msc_u8TextWidth + 1);
static const unsigned char msc_u8TextRowNum = msc_u8OledHeight
/ (msc_u8TextHeight + 1);

const unsigned char mc_u8SpiMode;
const unsigned char mc_u8SpiBitsWrite;
const unsigned char mc_u8SpiBitsRead;
const unsigned long mc_u32SpiSpeedHz;
const unsigned short mc_u16SpiDelay;
const unsigned char mc_u8CsChange;

CGpioControl m_gpioReset; //< P8-14(GPIO0_26->26)
CGpioControl m_gpioVccOn; //< P8-12(GPIO1_12->44)

int m_iColorForeground;
int m_iColorBackground;

unsigned char m_u8TextPositionColumn;
unsigned char m_u8TextPositionRow;

void init_spi();
void init_oled();
void shutdown_oled();
void sendCommand(unsigned char a_u8Command);
void sendData(unsigned char a_u8Data);
int send(unsigned short a_u16Buf);

/** Command list for the OLED controller */
enum {
SET_DISPLAY_MODE_ALL_OFF = 0xA4,
SET_DISPLAY_MODE_ALL_ON = 0xA5,
SET_DISPLAY_MODE_NORMAL = 0xA6,
SET_DISPLAY_MODE_INVERSE = 0xA7,
SET_COMMAND_LOCK = 0xFD,
SET_SLEEP_MODE_ON = 0xAE,
FRONT_CLOCK_DRIVER_OSC_FREQ = 0xB3,
SET_MUX_RATIO = 0xCA,
SET_DISPAY_OFFSET = 0xA2,
SET_DISPAY_START_LINE = 0xA1,
SET_REMAP_COLOR_DEPTH = 0xA0,
SET_GPIO = 0xB5,
FUNCTION_SELECTION = 0xAB,
SET_SEGMENT_LOW_VOLTAGE = 0xB4,
SET_CONTRAST_CURRENT_FOR_COLOR_ABC = 0xC1,
MASTER_CONTRAST_CURRENT_CONTROL = 0xC7,
LOOKUP_TABLE_FOR_GRAYSCALE_PULSE_WIDTH = 0xB8,
SET_RESET_PRECHARGE_PERIOD = 0xB1,
ENHANCE_DRIVING_SCHEME_CAPABILITY = 0xB2,
SET_PRECHARGE_VOLTAGE = 0xBB,
SET_SECOND_PRECHARGE_VOLTAGE = 0xB6,
SET_VCOMH_VOLTAGE = 0xBE,
SET_DISPLAY_MODE_RESET = 0xA6,
SET_COLUMN_ADDRESS = 0x15,
SET_ROW_ADDRESS = 0x75,
WRITE_RAM_COMMAND = 0x5C,
SET_SLEEP_MODE_OFF = 0xAF
};
};

#endif /* OLEDSPI_H_ */