動作環境
MSP430
概要
I2C接続の小型キャラクタLCD AQM1602をMSP430を用いて表示する。
- ソフトウェアI2Cにて実装
- P2.0: SCK
- P2.1: SDA
- 10kにてプルアップ
- AQM1602を使用
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程度待たないとクリア処理が正常に行われない場合がある。