MSP430 + AQM1602 > v0.1 > ソフトウェアI2Cを使って、とりあえず文字表示まで

```txt:動作環境
MSP430
```

## 概要

I2C接続の小型キャラクタLCD AQM1602をMSP430を用いて表示する。

- ソフトウェアI2Cにて実装
  - P2.0: SCK
  - P2.1: SDA
  - 10kにてプルアップ
- AQM1602を使用
  - http://akizukidenshi.com/catalog/g/gK-08896/

## v0.1

- main.cに全部書くという悪いコーディング
- string.hやstdint.hを使うというマイコンに良くない贅沢なコーディング
- とりあえず文字が表示された程度

ESP8266でAQM0802を使用した時はWire Libraryを使うことができた。
http://qiita.com/7of9/items/7fbfe85b62c58ce01a5d
そのコードをMSP430でソフトウェアI2cにして使うように実装した。

```main.c
/*
v0.1 2017/02/24 AQM1602試用
*/

//   ACLK = n/a, MCLK = SMCLK = DCO ~ 800kHz

#include <msp430x12x2.h>
#include <stdbool.h>
#include <math.h>
#include <stdint.h>
#include <string.h>

#define DELTA 488                           // target DCO = DELTA*(4096) ~2MHz

char s_rcvChar=0x00;

#define DATA_0_PORT BIT1
#define SCK_0_PORT BIT0

#define DATA_0_LOW      P2OUT &= ~DATA_0_PORT 
#define DATA_0_HIGH     P2OUT |= DATA_0_PORT 
#define SCK_0_LOW      P2OUT &= ~SCK_0_PORT 
#define SCK_0_HIGH     P2OUT |= SCK_0_PORT 

#define DATA_0_DIROUT   P2DIR |= DATA_0_PORT // direction OUT
#define DATA_0_DIRIN   P2DIR &= ~DATA_0_PORT // direction IN
#define SCK_0_DIROUT   P2DIR |= SCK_0_PORT // direction OUT

void delay_ms(int msec);

//#define DELAY_CYC      __delay_cycles(80)
#define DELAY_CYC      __delay_cycles(8)

char s_write_byte(unsigned char value_a);
void i2c_start(void);
void i2c_stop(void);
void AQM1602_PutMessage(uint8_t *srcPtr, int len, uint8_t x_st1, uint8_t y_st1);
void Test_AQM1602_cursorOn_posOff_contrastLow(void);
void Test_AQM1602_cursorOn_posOn_contrastLow(void);
void Test_AQM1602_cursorOn_posOn_contrastHigh(void);

static const uint8_t kDeviceAddr = 0x3e; // 7bit address
static const int kMaxXsize = 16;
static const int kMaxYsize = 2;
static const int kOffset_addr2ndline = 0x40;


static uint8_t ControlByteList[] = {
  0x00, // Instruction write operation. ( Co=0, Rs=0 )
  0x40, // Data write operation. ( Co=0, Rs=1 )
};
enum {
  TYPE_INSTRUCITON = 0,
  TYPE_DATA,
};

void delay_us(int usec)
{
    // きちんとした1usecでない    
    int idx;
    
    for(idx=0;idx<usec;idx++) {
        __delay_cycles(1);
    }
}

void delay_ms(int msec)
{
    // きちんとした1msecでない   
    int idx;
    
    for(idx=0;idx<msec;idx++) {
        __delay_cycles(800);
    }
}

void DATA_out(bool sts)
{     
    DATA_0_DIROUT;        
    if(sts) {
        DATA_0_HIGH;    
    } else {
        DATA_0_LOW;
    }
}
void SCK_out(bool sts)
{
    SCK_0_DIROUT;        
    if(sts) {
        SCK_0_HIGH;    
    } else {
        SCK_0_LOW;
    }    
}

bool is_DATA_0_High(void)
{
    bool bfBuf;
    
    bfBuf = (bool)((P2IN & DATA_0_PORT) > 0);    
    return bfBuf ;    
}

bool DATA_in(void)
{    
    bool bfBuf;

    DATA_0_DIRIN;
    DELAY_CYC;
    bfBuf = is_DATA_0_High();
    
    return bfBuf;
}

void i2c_beginTransmission(uint8_t devAdr_7bit)
{
    uint8_t adr = (devAdr_7bit << 1);
    i2c_start();
    s_write_byte(adr);
}
void i2c_endTransmission(void)
{
    i2c_stop();
}
void i2c_write(uint8_t data)
{
    s_write_byte(data);
}

void writeToDevice(int type, uint8_t *dataByte, size_t len) 
{
    int idx;
    for (idx = 0; idx < len; idx++) {
        i2c_beginTransmission(kDeviceAddr);
        i2c_write(ControlByteList[type]);
        i2c_write(dataByte[idx]);
        i2c_endTransmission();
        // delayMicroseconds(27); // 26.3us
        DELAY_CYC;
    }
}

void AQM1602_WriteSingleInstruction(uint8_t data) 
{ 
  size_t len = 1;
  uint8_t list[] = {data};
  writeToDevice(TYPE_INSTRUCITON, list, len); 
}

void AQM1602_WriteInstructions(uint8_t *data, int len) 
{ 
  writeToDevice(TYPE_INSTRUCITON, data, len); 
}

void AQM1602_WriteData(uint8_t *data, size_t len) 
{ 
  writeToDevice(TYPE_DATA, data, len); 
}

void AQM1602_Clear()
{
    AQM1602_WriteSingleInstruction(0x01);
}

void AQM1602_Initialize(bool cursorOn, bool cursorPosOn, uint8_t contrast)
{ 
  // 
  delay_ms(40); // Wait time > 40ms after VDD stable

  // Function set
  AQM1602_WriteSingleInstruction(0x38);

  // Function set
  AQM1602_WriteSingleInstruction(0x39);

  // Internal OSC frequency
  AQM1602_WriteSingleInstruction(0x14);

  // { Contrast set -----------------------------
  uint8_t ctrst = contrast;
  if (ctrst > 0xF) {
    ctrst = 0xF;
  }
  AQM1602_WriteSingleInstruction(0x70 | ctrst);
  // } Contrast set -----------------------------

  // Power/ICON/Contrast control
  AQM1602_WriteSingleInstruction(0x56);

  // Follower control
  AQM1602_WriteSingleInstruction(0x6C);

  // Wait time > 200mS (for power stable)
  delay_ms(200);

  // Function set
  AQM1602_WriteSingleInstruction(0x38);

  // { Display ON/OFF control -----------------------
  uint8_t extra = 0x0;
  if (cursorOn) {
    extra = extra | 0x2;
  }
  if (cursorPosOn) {
    extra = extra | 0x3;    
  }
  AQM1602_WriteSingleInstruction(0x0C | extra);
  // } Display ON/OFF control -----------------------

  // Clear Display
  AQM1602_WriteSingleInstruction(0x01);

  // Wait time > 1.08ms
  delay_ms(2);
}

void uartPC_init(void)
{
    
    P3SEL |= 0x30;                            // P3.4,5 = USART0 TXD/RXD
    ME2 |= UTXE0 + URXE0;                     // Enable USART0 TXD/RXD
    UCTL0 |= CHAR;                            // 8-bit character
    UTCTL0 |= SSEL0;                          // UCLK = ACLK
    UBR00 = 0x03;                             // 32k/9600 - 3.41
    UBR10 = 0x00;                             //
    UMCTL0 = 0x4A;                            // Modulation
    UCTL0 &= ~SWRST;                          // Initialize USART state machine
    IE2 |= URXIE0;                            // Enable USART0 RX interrupt  
}

void uartPC_txBuf(unsigned char *pstr) 
{
    static int widx;
    
    widx = 0;
    while(1) {
        if(pstr[widx] == '\0') break;        
        while(!(IFG2&UTXIFG0))
            ;
        TXBUF0 = pstr[widx++];
    }
}

#define LCD_ADDRESS_8bit (0x7C) // 8ビットにしたアドレス (0x3E << 1)

void i2c_start(void) 
{
    SCK_out(1);
    DELAY_CYC;
    DATA_out(1);
    DELAY_CYC;
    DATA_out(0);
    DELAY_CYC;
    SCK_out(0);
    DELAY_CYC;
    
    // wait more than 1.3usec
    delay_us(2);
}

void i2c_stop(void)
{
    DATA_out(0);
    DELAY_CYC;
    SCK_out(1);
    DELAY_CYC;
    DATA_out(1);
    DELAY_CYC;
}

void main(void)
{           
    WDTCTL = WDT_ADLY_1000;                    // WDT 1000ms, ACLK, interval timer
        
    DATA_0_HIGH;
    SCK_0_LOW;
    
    P1DIR |= BIT2;
    P1OUT |= BIT2;

    DATA_out(1);
    SCK_out(1);
    
    _BIS_SR(GIE);    
    
    uartPC_init();  
	    
    IE1 |= WDTIE;                             // Enable WDT interrupt
    
    AQM1602_Initialize(/* cursorOn=*/true, /* cursorPosOn=*/true, /* contrast=*/7);

    for(int loop=0; loop<100; loop++) {         
        AQM1602_PutMessage("...........ready", strlen("...........ready"), /* x_st1=*/1, /* y_st1=*/1);
        delay_ms(200);
    }
    
	while(1)
	{
        __bis_SR_register(LPM3_bits + GIE);    
	}   // while(1)
 
}

char s_write_byte(unsigned char value_a)
{    
    int loop;
    
    for(loop=0; loop<8; loop++) {
        if (value_a & 0x80) {
            DATA_out(1);
        } else {
            DATA_out(0);
        }
        
        SCK_out(1);
        DELAY_CYC;
        SCK_out(0);
        DELAY_CYC;
        value_a <<= 1;
    }

    SCK_out(1);
    DELAY_CYC;
    DATA_in(); // for ACK
    SCK_out(0);
    DELAY_CYC;

    return 0;    
}

#pragma vector=USART0RX_VECTOR
__interrupt void usart0_rx (void)
{
    
    s_rcvChar = RXBUF0;
    _NOP();
    
}

void AQM1602_PutMessage(uint8_t *srcPtr, int len, uint8_t x_st1, uint8_t y_st1)
{
  // _st1 : index starting from 1
  if ((x_st1 > kMaxXsize) || (y_st1 > kMaxYsize)) {
    return; // error
  }

  uint8_t pos;

  if (len <= kMaxXsize) {
    pos = 0x80 | ((y_st1 - 1) * kOffset_addr2ndline);
    pos = pos | (x_st1 - 1);
    AQM1602_WriteSingleInstruction(pos);
    AQM1602_WriteData( srcPtr, len );
    return;
  }
}

#pragma vector=WDT_VECTOR
__interrupt void watchdog_timer(void)
{

   __bic_SR_register_on_exit(LPM3_bits);

}

// test functions ---------------------------

void Test_AQM1602_cursorOn_posOff_contrastLow(void)
{
    AQM1602_Initialize(/* cursorOn=*/true, /* cursorPosOn=*/false, /* contrast=*/1);
}

void Test_AQM1602_cursorOn_posOn_contrastLow(void)
{
    AQM1602_Initialize(/* cursorOn=*/true, /* cursorPosOn=*/true, /* contrast=*/1);
}

void Test_AQM1602_cursorOn_posOn_contrastHigh(void)
{
    AQM1602_Initialize(/* cursorOn=*/true, /* cursorPosOn=*/true, /* contrast=*/15);
}

void Test_AQM1602_writeTest(void)
{
    const char *tsgMsg = "HELLO";
    AQM1602_PutMessage((uint8_t *)tsgMsg, strlen(tsgMsg), /* x_st1=*/1, /* y_st1=*/1);  
}
```

### memo
(追記 2017/03/29)

`AQM1602_Clear()`実行後は10msec程度待たないとクリア処理が正常に行われない場合がある。