元高専生のロボット作り

元高専生のロボット作り

主にプログラミング, 電子系について書きます。たまに機械系もやります。メモ代わりの記事ばっか書きます

I2C対応のLCDをPICから動かす

AmazonでI2Cで操作できるLCDモジュールが650円で売ってます。
I2C対応のLCDモジュールでは一番安いのでは?


ArduinoではLiquidCrystal_I2Cというライブラリを使用すれば簡単に動かせます。

今回はこれをPICで動かしました。

PICはPIC24FV32KA302を使用して、MPLABXでコードを書きます。


やりかた

New Projectから空のプロジェクトを作成します。

まずは、MCCを使用してI2C周りの設定を行います。

MCCを開いたら、Device ResourcesからI2C→I2C1[PIC24/dsPIC33/PIC32MM .......]を選択し、Project Resourcesに追加します。

I2Cの詳しい設定はだいたいデフォルトでよかったはず。

一応動いたときのスクショ

f:id:sgrsn1711:20190516164903p:plain

ModeはMasterで100kHzね。

Pin ModuleはWPUにチェック。

f:id:sgrsn1711:20190516165007p:plain

そしたらProject Resources欄のGenerateをクリックして設定完了。

コード

動いたコードから切り貼りしたので抜けとかあるかも
文句くれればすぐ直す

多分誰もやんないし大丈夫でしょ

LCD.h

#include <stdio.h>
#include <stdlib.h>
#include <xc.h>

// commands
#define LCD_CLEARDISPLAY 0x01
#define LCD_RETURNHOME 0x02
#define LCD_ENTRYMODESET 0x04
#define LCD_DISPLAYCONTROL 0x08
#define LCD_CURSORSHIFT 0x10
#define LCD_FUNCTIONSET 0x20
#define LCD_SETCGRAMADDR 0x40
#define LCD_SETDDRAMADDR 0x80

// flags for display entry mode
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00

// flags for display on/off control
#define LCD_DISPLAYON 0x04
#define LCD_DISPLAYOFF 0x00
#define LCD_CURSORON 0x02
#define LCD_CURSOROFF 0x00
#define LCD_BLINKON 0x01
#define LCD_BLINKOFF 0x00

// flags for display/cursor shift
#define LCD_DISPLAYMOVE 0x08
#define LCD_CURSORMOVE 0x00
#define LCD_MOVERIGHT 0x04
#define LCD_MOVELEFT 0x00

// flags for function set
#define LCD_8BITMODE 0x10
#define LCD_4BITMODE 0x00
#define LCD_2LINE 0x08
#define LCD_1LINE 0x00
#define LCD_5x10DOTS 0x04
#define LCD_5x8DOTS 0x00

// flags for backlight control
#define LCD_BACKLIGHT 0x08
#define LCD_NOBACKLIGHT 0x00

#define En 0b00000100  // Enable bit
#define Rw 0b00000010  // Read/Write bit
#define Rs 0b00000001  // Register select bit

#define LCD_ADD 0x27

void LCD_Init();
void begin(uint8_t cols, uint8_t lines, uint8_t dotsize);
void display();
void clear();
void home();
void noBacklight();
void backlight();
void setCursor(uint8_t col, uint8_t row);
size_t print(const uint8_t *buffer, size_t size);
void command(uint8_t value);
void send(uint8_t value, uint8_t mode);
void write4bits(uint8_t value);
void expanderWrite(uint8_t _data);
void pulseEnable(uint8_t _data);
void printIIC(uint8_t value);
inline size_t write(uint8_t value);

LCD.c

#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include "LCD.h"
#include "defined.h"
#include "typedefs.h"

#include "mcc_generated_files/i2c1.h"

#define FCY 2000000UL

#include <libpic30.h>

#include "mcc_generated_files/system.h"

uint8_t _Addr;
uint8_t _displayfunction;
uint8_t _displaycontrol;
uint8_t _displaymode;
uint8_t _numlines;
uint8_t _cols;
uint8_t _rows;
uint8_t _backlightval;

I2C1_MESSAGE_STATUS status;

void LCD_Init(){            //LCD????
    _Addr = LCD_ADD;
    _cols = 16;
    _rows = 2;
    _backlightval = LCD_NOBACKLIGHT;
    _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
    begin(_cols, _rows, LCD_5x8DOTS);
}

void begin(uint8_t cols, uint8_t lines, uint8_t dotsize) {
	if (lines > 1) {
		_displayfunction |= LCD_2LINE;
	}
	_numlines = lines;

	if ((dotsize != 0) && (lines == 1)) {
		_displayfunction |= LCD_5x10DOTS;
	}

	__delay_ms(50); 
  
	expanderWrite(_backlightval);	// backlight off
	__delay_ms(1000);

  	//put the LCD into 4 bit mode
	// this is according to the hitachi HD44780 datasheet
	// figure 24, pg 46

	write4bits(0x03 << 4);
  	 __delay_us(4500);
	write4bits(0x03 << 4);
	__delay_us(4500);
	write4bits(0x03 << 4); 
	__delay_us(150);
	write4bits(0x02 << 4);
    
	command(LCD_FUNCTIONSET | _displayfunction);  
	_displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
	display();
	clear();
	_displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
	command(LCD_ENTRYMODESET | _displaymode);
	home();
}

void display() {
	_displaycontrol |= LCD_DISPLAYON;
	command(LCD_DISPLAYCONTROL | _displaycontrol);
}

void clear(){
	command(LCD_CLEARDISPLAY);// clear display, set cursor position to zero
	__delay_us(2000);  // this command takes a long time!
}

void home(){
	command(LCD_RETURNHOME);  // set cursor position to zero
	__delay_us(2000);  // this command takes a long time!

}

void noBacklight() {
	_backlightval=LCD_NOBACKLIGHT;
	expanderWrite(0);
}

void backlight() {
	_backlightval=LCD_BACKLIGHT;
    expanderWrite(0);
}

void setCursor(uint8_t col, uint8_t row){
	int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
	if ( row > _numlines ) {
		row = _numlines-1;    // we count rows starting w/0
	}
	command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
}

size_t print(const uint8_t *buffer, size_t size){
  size_t n = 0;
  while (size--) {
    if (write(*buffer++)) n++;
    else break;
  }
  return n;
}

void command(uint8_t value) {
	send(value, 0);
}

void send(uint8_t value, uint8_t mode) {
	uint8_t highnib=value&0xf0;
	uint8_t lownib=(value<<4)&0xf0;
        write4bits((highnib)|mode);
	write4bits((lownib)|mode);
}

void write4bits(uint8_t value) {
	expanderWrite(value);
    __delay_ms(1);
	pulseEnable(value);
    __delay_ms(1);
}

uint8_t writeBuffer[3] = {};

void pulseEnable(uint8_t _data){
	expanderWrite((uint8_t)(_data | 0b00000100));	// En high
    __delay_us(100);    //on 1us, sent wrong value...
	
	expanderWrite((uint8_t)_data & (~En));	// En 
    __delay_us(100);    //on 1us, sent wrong value...
}

void expanderWrite(uint8_t _data){
    writeBuffer[0] = (uint8_t)((_data) | _backlightval);
    I2C1_MasterWrite(writeBuffer, 1, LCD_ADD, &status);
}

inline size_t write(uint8_t value){
	send(value, Rs);
	return 1;
}

メイン
main.c

#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include "LCD.h"

#include "mcc_generated_files/i2c1.h"

#define FCY 2000000UL

#include <libpic30.h>

#include "mcc_generated_files/system.h"

int main(void)
{
    I2C1_Initialize();
    LCD_Init();
    backlight();

    setCursor(0, 0);
    print("Hello", 5);
    setCursor(0, 1);
    print("World", 5);
    
    return 1;
}