2018年7月16日月曜日

使いそびれたパラレルキャラクタ液晶のSPI化(1/2)

ずっと昔にまとめ買いした「LCDキャラクタディスプレイモジュール」(秋月電子のSC1602BSLB-XA-GB-KとかSD1602HULB-XA-G-G)が使いそびれていた。
5Vだしパラレル入力だし・・・。
最近では、3VとかI2C通信のものが安価に出ている。
困った。 FON2405E(Linuxボード)は 3Vだし、今のところSPIしか使えていない。
そこで、PICマイコン使って
  • インターフェース変換(SPI to パラレル)
  • 3V駆動
  • SW入力
  • バックライト
を目指す。

先に結論↓ こんな感じで、Linuxボードから制御してみた。



1. インターフェース変換

SPI搭載の手持ちPICマイコン=PIC16F818 / 819 にした。
まぁシフトレジスタでも実現できるような気がするけど。
http://www.aitendo.com/product/14623
だけど3V駆動化とかSW入力とかやりたいので、今回はPICにこだわった。


2. 3V駆動

いろいろな方が実現している。感謝!
PICにてPWM出力し、チャージポンプにて負電圧を生成する。
最初に思いついた方すごいね!
http://elm-chan.org/docs/lcd/lcd3v_j.html
http://jsdiy.web.fc2.com/lcd3v/


3. SW入力

入力ピンが足りないので、アナログ入力+抵抗分圧にて。
こちらもいろいろな方が実現している。感謝。
https://synapse.kyoto/hard/keypad/page001.html


4. バックライト

バックライト駆動するためには4V 40mA程度必要っぽい。
2のチャージポンプでは賄えないか・・・。
要検討・・・。

んでもって、現状の回路図はこんな感じ。


ソースコード
/*
* File: lcd_lib.c
* Author: adeno
*
* Created on 2018/06/25, 1:18
*/
#include <xc.h>
#include "lcd_lib.h"
/*
*
*/
void lcd_change_col(unsigned char i){
// if(i == 1) { lcd_cmd(0xC0); }
// else {lcd_cmd(0x80);}
if(i == 1){
lcd_out(0xC0,0);
lcd_out(0x00,0); //<<4
}else{
lcd_out(0x80,0);
lcd_out(0x00,0); //<<4
}
__delay_ms(2);
}
///
///LCDにコマンド・文字を出力
/// charflg=0 コマンド、charflg=1 文字
/// 要指定
/// #define lcd_e_bit
/// #define lcd_rs_bit
/// #define lcd_port PORTA この場合、RA3 - 0を使う
void lcd_out(unsigned char code,unsigned char charflg){
unsigned char port_bak;
unsigned char rs_bit = (1 << lcd_rs_bit);
unsigned char e_bit = (1 << lcd_e_bit);
//データ出力
port_bak = lcd_port & 0b11100001; //RA4 - 1
port_bak = port_bak | (0b00011110 & (code >> 3));
lcd_port = port_bak;
//rs set
port_bak = port_bak & (~rs_bit);
if (charflg == 1){
//RSをhigh
port_bak = port_bak | rs_bit;
}
lcd_port = port_bak;
//eにワンショット
port_bak = port_bak & (~e_bit);
lcd_port = port_bak | e_bit;
__nop();
lcd_port = port_bak;
}
void lcd_clear(){
lcd_cmd(0x01);
__delay_ms(15);
}
void lcd_init(){
//LCDの初期化
__delay_ms(15);
lcd_out(0x30,0);
__delay_ms(5);
lcd_out(0x30,0);
__delay_ms(1);
lcd_out(0x30,0);
__delay_ms(1);
lcd_out(0x20,0);
__delay_ms(1);
lcd_cmd(0x2e);
// lcd_out(0x2e,0);
// lcd_out(0xe0,0); //<<4
// __delay_ms(2);
lcd_cmd(0x08);
lcd_cmd(0x0d);
lcd_cmd(0x06);
lcd_cmd(0x02);
}
view raw lcd_lib.c hosted with ❤ by GitHub
/*
* File: lcd_lib.h
* Author: adeno
*
* Created on 2018/06/25, 1:20
*/
#ifndef LCD_LIB_H
#define LCD_LIB_H
#ifdef __cplusplus
extern "C" {
#endif
//出力にラッチがないPIC向けで、データと制御のポート群が同じ場合
//データ出力がRx4〜Rx1の場合
#define _XTAL_FREQ 8000000 //delay用に必要(クロック8MHzを指定)
//RA1 D4
//RA2 D5
//RA3 D6
//RA4 D7
//RA4 E
//RA6 RS
#define LCD_ROW 16
#define LCD_COL 2
#define lcd_port PORTA
#define lcd_e_bit 7
#define lcd_rs_bit 6
#define lcd_data(ascii) lcd_out(ascii,1);lcd_out(ascii << 4,1);__delay_us(50);
#define lcd_cmd(cmd) lcd_out(cmd,0);lcd_out(cmd << 4,0);__delay_ms(2);
void lcd_change_col(unsigned char i);
void lcd_out(unsigned char code,unsigned char charflg);
void lcd_init();
void lcd_clear();
#ifdef __cplusplus
}
#endif
#endif /* LCD_LIB_H */
view raw lcd_lib.h hosted with ❤ by GitHub
/*
* File: main.c
* Author: adeno
*
* Created on 2018/06/05, 3:25
*/
// CONFIG
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB3 // CCP1 Pin Selection bit (CCP1 function on RB3)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
//#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "lcd_lib.h"
__EEPROM_DATA('S','p','i','C','h','a','r','L');
__EEPROM_DATA('C','D',' ','v','0','.','4',' ');
//PWM設定(16-)
__EEPROM_DATA(0b01,166, 83,0,1,0,0,0); //16 {0b01,166, 83,0,1}, //3kHz
__EEPROM_DATA(0b01,199,100,0,0,0,0,0); //24 {0b01,199,100,0,0}, //2.5kHz
__EEPROM_DATA(0b01,249,125,0,0,0,0,0); //32 {0b01,249,125,0,0}, //2kHz
__EEPROM_DATA(0b10, 82, 42,1,0,0,0,0); //40 {0b10, 82, 42,1,0}, //1.5kHz
__EEPROM_DATA(0b10,124, 62,1,0,0,0,0); //48 {0b10,124, 62,1,0}, //1kHz
__EEPROM_DATA(0b10,138, 69,0,1,0,0,0); //56 {0b10,138, 69,0,1}, //0.9kHz
__EEPROM_DATA(0b10,155, 78,0,0,0,0,0); //64 {0b10,155, 78,0,0}, //0.8kHz
void initPWM(void);
//void changePWMindex(unsigned char i);
//void changePWM(int ps,int pr,int duty,int d1y,int d1x);
void changePWMeepromindex(unsigned char i);
void initLCD(void);
void initAD(void);
void initSPI(void);
void initTMR0(void);
unsigned char getAD();
#define RBUFSIZE 32
#define RBUFMASK 0x1f
#define RCV_STATE_FUNC 0
#define RCV_STATE_CPWM 1
#define RCV_STATE_CCMD 2
#define RCV_STATE_CDATA 3
#define RCV_CMD_BKLIGHT 0x00
#define RCV_CMD_PWM 0x01
#define RCV_CMD_LCDCMD 0x02
#define RCV_CMD_LCDDATA 0x03
#define RCV_CMD_SW 0x04
#define RCV_CMD_LCDDATAFIN 0x00
#define RCV_DATA_PWM_0 16
#define RCV_DATA_PWM_1 24
#define RCV_DATA_PWM_2 32
#define RCV_DATA_PWM_3 40
#define RCV_DATA_PWM_4 48
#define RCV_DATA_PWM_5 56
#define RCV_DATA_PWM_6 64
#define GTIMER 131
unsigned char ringbuf[RBUFSIZE];
unsigned char wpoi = 0;
unsigned char rpoi = 0;
unsigned char rcvstate = 0;
unsigned char swadcflg = 0;
//struct pwmsetting {
// unsigned char ps;
// unsigned char pr;
// unsigned char duty;
// bool d1y;
// bool d1x;
//};
//struct pwmsetting pwmary[7] = {
// //ps, pr, duty, d1y, d1x
// {0b01,166, 83,0,1}, //3kHz
// {0b01,199,100,0,0}, //2.5kHz
// {0b01,249,125,0,0}, //2kHz
// {0b10, 82, 42,1,0}, //1.5kHz
// {0b10,124, 62,1,0}, //1kHz
// {0b10,138, 69,0,1}, //0.9kHz
// {0b10,155, 78,0,0}, //0.8kHz
// };
void putch(unsigned char c){
lcd_data(c);
return;
}
/*
*
*/
int main(/*int argc, char** argv*/) {
OSCCONbits.IRCF = 0b111; //8MHz
initPWM();
initAD();
initSPI();
initLCD();
initTMR0();
unsigned char adv;
// unsigned char i;
unsigned char oldsw = 0;
unsigned char sw = 0;
lcd_change_col(1);
TMR0 = GTIMER;
INTCONbits.TMR0IE = 1;
while(1){
if(swadcflg == 1){
swadcflg = 0;
adv = getAD();
if(adv > 21 && adv < 26){
//SW1
sw = 1;
}else if(adv > 54 && adv < 64){
//SW2
sw = 2;
}else if(adv > 105 && adv < 118){
//SW3
sw = 3;
}else if(adv > 157 && adv < 170){
//SW4
sw = 4;
}else if(adv > 210 && adv < 218){
//SW5
sw = 5;
}else if(adv > 238 && adv < 242){
//SW6
sw = 6;
}else{
sw = 0;
}
if(sw != oldsw){
lcd_data(sw+'0');
}
oldsw = sw;
}
// __delay_ms(1);
if(wpoi != rpoi){
if(rcvstate == RCV_STATE_FUNC){
if(ringbuf[rpoi] == RCV_CMD_PWM) { rcvstate = RCV_STATE_CPWM; }
else if(ringbuf[rpoi] == RCV_CMD_LCDDATA) { rcvstate = RCV_STATE_CDATA; }
else if(ringbuf[rpoi] == RCV_CMD_LCDCMD) { rcvstate = RCV_STATE_CCMD; }
}else if(rcvstate == RCV_STATE_CPWM){
sw = 0;
if(ringbuf[rpoi] == 0) { sw = RCV_DATA_PWM_0;}
else if(ringbuf[rpoi] == 1) { sw = RCV_DATA_PWM_1;}
else if(ringbuf[rpoi] == 2) { sw = RCV_DATA_PWM_2;}
else if(ringbuf[rpoi] == 3) { sw = RCV_DATA_PWM_3;}
else if(ringbuf[rpoi] == 4) { sw = RCV_DATA_PWM_4;}
else if(ringbuf[rpoi] == 5) { sw = RCV_DATA_PWM_5;}
else if(ringbuf[rpoi] == 6) { sw = RCV_DATA_PWM_6;}
changePWMeepromindex(sw);
rcvstate = RCV_STATE_FUNC;
}else if(rcvstate == RCV_STATE_CCMD){
lcd_cmd(ringbuf[rpoi]);
rcvstate = RCV_STATE_FUNC;
// if(ringbuf[rpoi] == RCV_CMD_LCDDATAFIN) { rcvstate = RCV_STATE_FUNC;}
// else{lcd_cmd(ringbuf[rpoi]);}
}else if(rcvstate == RCV_STATE_CDATA){
lcd_data(ringbuf[rpoi]);
rcvstate = RCV_STATE_FUNC;
// if(ringbuf[rpoi] == RCV_CMD_LCDDATAFIN) { rcvstate = RCV_STATE_FUNC;}
// else{lcd_data(ringbuf[rpoi]);}
}
rpoi = (rpoi+1) & RBUFMASK;
}
//
// __delay_ms(2000);
// changePWMindex(0);
// lcd_change_col(1);
// // 0123456789ABCDEF
// printf("PWM> 3kHz ");
//
// __delay_ms(2000);
// changePWMindex(1);
// lcd_change_col(1);
// // 0123456789ABCDEF
// printf("PWM> 2.5kHz ");
//
// for(i=0;i<100;i++){
// __delay_ms(200);
// adv = getAD();
// lcd_change_col(1);
// printf("AD> %d ",adv);
// }
//
// __delay_ms(2000);
// changePWMindex(2);
// lcd_change_col(1);
// // 0123456789ABCDEF
// printf("PWM> 2kHz ");
//
// __delay_ms(2000);
// changePWMindex(3);
// lcd_change_col(1);
// // 0123456789ABCDEF
// printf("PWM> 1.5kHz ");
//
//// __delay_ms(2000);
//// changePWMindex(4);
//// lcd_change_col(1);
//// // 0123456789ABCDEF
//// printf("PWM> 1kHz ");
////
//// __delay_ms(2000);
//// changePWMindex(5);
//// lcd_change_col(1);
//// // 0123456789ABCDEF
//// printf("PWM> 0.9kHz ");
////
//// __delay_ms(2000);
//// changePWMindex(6);
//// lcd_change_col(1);
//// // 0123456789ABCDEF
//// printf("PWM> 0.8kHz ");
}
return (EXIT_SUCCESS);
}
void initLCD(void){
//RA1 D4
//RA2 D5
//RA3 D6
//RA4 D7
//RA6 RS
//RA7 E
TRISAbits.TRISA1 = 0;
TRISAbits.TRISA2 = 0;
TRISAbits.TRISA3 = 0;
TRISAbits.TRISA4 = 0;
TRISAbits.TRISA6 = 0;
TRISAbits.TRISA7 = 0;
ADCON1 = 0b1110; //4-1:D 0:A
__delay_ms(100);
//LCDの初期化
lcd_init();
//LCDの表示クリア
lcd_clear();
// dist = DIST_LCD;
// 0123456789ABCDEF
// printf("SpiCharLCD v0.1a");
// lcd_change_col(1);
// printf(">");
// lcd_cmd(0xc0);
unsigned char i,d;
for(i=0;i<16;i++){
d = eeprom_read(i);
lcd_data(d);
}
// lcd_data('S'); lcd_data('p'); lcd_data('i');
// lcd_data('C'); lcd_data('h'); lcd_data('a'); lcd_data('r');
// lcd_data('L'); lcd_data('C'); lcd_data('D'); lcd_data(' ');
// lcd_data('v'); lcd_data('0'); lcd_data('.'); lcd_data('1'); lcd_data('b');
}
void initPWM(void){
//PWM
//1)Set the PWM period by writing to the PR2 register.
//周期 = ( PR2 + 1 ) x 4 x 1クロック分の時間 x プリスケーラ値
//10kHz 周期0.1ms = (PR2+1)x4x(1/8MHz)xPrescale
// 10^-4 = S*4*(1/8)*10^-6*P
// 2*10^2 = S*P
// 200 = (PR2+1)*P --- P=1 PR2=199
CCP1CON = 0b00001100; //PWMを使用
// T2CONbits.T2CKPS = 0b00; //プリスケーラ値1
// PR2 = 199;
//2)Set the PWM duty cycle by writing to the
//CCPR1L register and CCP1CON<5:4> bits.
//PWM Duty Cycle = (CCPR1L:CCP1CON<5:4>) •TOSC • (TMR2 Prescale Value)
//50% 0.1ms*50% 0.05ms=50us
//PWM Duty Cycle = V * (1/8MHz) * 1
//DC = V * 1/8 * 10^-6
//50*10^-6 = V*1/8*10^-6
//400 = V
//Vの下位2bitはCCP1x-CCP1Y
//vの上位8bitはCCPR1L
// CCPR1L = 100;
// CCP1CONbits.CCP1Y = 0;
// CCP1CONbits.CCP1X = 0;
//3)Make the CCP1 pin an output by clearing the TRISB<x> bit.
TRISBbits.TRISB3 = 0;
//4)Set the TMR2 prescale value and enable Timer2 by writing to T2CON.
// T2CONbits.TMR2ON = 1;
//50%
//プリスケーラ PR2 CCPR1L CCP1Y CCP1X
//10kHz 1 00 199 100 0 0
//5kHz 4 01 99 50 0 0
//1kHz 16 1x 124 62.5 1 0
// changePWMindex(3);
changePWMeepromindex(40);
}
//void changePWMindex(unsigned char i){
// T2CONbits.TMR2ON = 0;
// T2CONbits.T2CKPS = pwmary[i].ps; //プリスケーラ値1
// PR2 = pwmary[i].pr;
// CCPR1L = pwmary[i].duty;
//
// CCP1CONbits.CCP1Y = pwmary[i].d1y;
// CCP1CONbits.CCP1X = pwmary[i].d1x;
// T2CONbits.TMR2ON = 1;
//}
void changePWMeepromindex(unsigned char i){
T2CONbits.TMR2ON = 0;
T2CONbits.T2CKPS = eeprom_read(i); //プリスケーラ値1
PR2 = eeprom_read(i+1);
CCPR1L = eeprom_read(i+2);
CCP1CONbits.CCP1Y = eeprom_read(i+3);
CCP1CONbits.CCP1X = eeprom_read(i+4);
T2CONbits.TMR2ON = 1;
}
//void changePWM(int ps,int pr,int duty,int d1y,int d1x){
// T2CONbits.TMR2ON = 0;
// T2CONbits.T2CKPS = (unsigned int)ps; //プリスケーラ値1
// PR2 = (unsigned int)pr;
// CCPR1L = (unsigned int)duty;
// CCP1CONbits.CCP1Y = (unsigned int)d1y;
// CCP1CONbits.CCP1X = (unsigned int)d1x;
// T2CONbits.TMR2ON = 1;
//}
void initAD(){
ADCON1 = 0b1110; //4-1:D 0:A
//16TOSC 1 01 10MHz
ADCON1bits.ADCS2 = 1;
ADCON1bits.ADFM = 0; //左づめ
// ADCON0bits.ADCS = 0b01;
// ADCON0bits.CHS = 0b000;
// ADCON0bits.ADON = 1;
ADCON0 = 0b01000001;
}
unsigned char getAD(){
ADCON0bits.GO = 1;
__delay_us(20);
while(ADCON0bits.GO){
}
return ADRESH;
}
void initSPI(){
SSPCONbits.SSPM = 0b0100;
SSPCONbits.SSPEN = 1;
TRISBbits.TRISB1 = 1; //SDI
TRISBbits.TRISB2 = 0; //SDO
TRISBbits.TRISB4 = 1; //SCK
TRISBbits.TRISB5 = 1; //SS
//mode SSPCON(CKP) SSPSTAT(CKE)
//1 0,1 0 0
//3 1,1 1 0
//0 0,0 0,1
//2 1,0 1,1
SSPCONbits.CKP = 1;
SSPSTATbits.CKE = 0;
PIE1bits.SSPIE = 1;
INTCONbits.PEIE = 1;
INTCONbits.GIE = 1;
}
void interrupt ISR(void)
{
if(PIR1bits.SSPIF == 1){
PIR1bits.SSPIF = 0;
ringbuf[wpoi] = SSPBUF;
wpoi = (wpoi+1) & RBUFMASK;
// TMR0 = GTIMER;
// INTCONbits.TMR0IE = 1;
}else if(INTCONbits.TMR0IF == 1){
//rcvstate = 10;
swadcflg = 1;
TMR0 = GTIMER;
INTCONbits.TMR0IF = 0;
}
//lcd_data(SSPBUF);
}
void initTMR0(void){
OPTION_REGbits.PS = 0b011;
OPTION_REGbits.PSA = 0;
OPTION_REGbits.T0CS = 0;
}
view raw main.c hosted with ❤ by GitHub

0 件のコメント:

コメントを投稿