LoginSignup
0
3

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-02-24
動作環境
MSP430

概要

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

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程度待たないとクリア処理が正常に行われない場合がある。

0
3
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3