無事、大気圧をキャラクタLCDに表示できるようになりました。
このために、AVRのハードTWI機能をつかったI2Cのコーディング、負電圧生成で5V用LCDを3.3Vで使う、cコードのファイル分割、ということをしました。c言語のオブジェクト指向風記述をしようとしましたが、抽象化が中途半端なところで諦めました。
レジスタから読んだデータをシフトしてuint32_tに代入するときに、予めキャストをしていなかったせいで、シフトでデータが消えているというバグをつぶすのに時間がかかりました。あとはsprintfで%ul,%d,%uの使い間違いとかのバグもあった。そしてI2Cのエラー処理を追加しました。
コードはこちら。長いです。
main.c
int main(void){
psens s = psens_create(SENS_ADDR);
lcd l = lcd_create();
char str[9];//8char + \0
while(1){
sprintf(str, "%7.2fh", psens_getpressure(s));
lcd_home_pos(l);
lcd_write_str(l, str);
sprintf(str, "Pa%4.1f%cC", psens_gettemp(s), 0xdf);
lcd_second_pos(l);
lcd_write_str(l, str);
_delay_ms(1000);
}
return 0;
}
psens.h
/*
* psens.h
*/
#ifndef __PSENS_H__
#define __PSENS_H__
/* i2c address of the pressure sensor */
//#define ADDR 0b1011100 // 7 bit
//psens()
/* port of avr */
#define SCL PORTC5
#define SDA PORTC4
//only used for pullup
/* member */
typedef struct psens_struct* psens;
psens psens_create(uint8_t addr);
void psens_destroy(psens s);
float psens_getpressure(psens s);
float psens_gettemp(psens s);
#endif
psens.c
#include <avr/io.h>
#include <util/delay.h>
#include <util/twi.h>
#include <stdlib.h>
#include "psens.h"
/* I2C */
#define R 1
#define W 0
/* mask for status register */
#define MASK 0xf8
/* cmd list */
#define WHO_AM_I 0x0f
#define CTRL_REG1 0x20
#define PD 7 // power down when 0
#define BDU 2 // block data update
#define ODR 4
#define CTRL_REG2 0x21
#define ONE_SHOT 0
#define SRT 2
#define BOOT 7
/* register list */
#define CTRL_REG3 0x22
#define STATUS_REG 0x27
#define P_DA 1
#define T_DA 0
#define PRESS_OUT 0x28
#define TEMP_OUT 0x2b
/* macro cmds */
#define TW_WAIT() while (!(TWCR & (1 << TWINT)));//wait
#define TW_STOP() TWCR = (1 << TWINT) | (1 << TWSTO)| (1 << TWEN); //STOP
int32_t read(psens s, uint8_t reg_add, uint8_t bits);
int8_t write_one(psens s, uint8_t reg_add, uint8_t dat);
struct psens_struct{
//port_t scl;
//port_t sda;
uint8_t addr;
};
void psens_init(psens s){
PORTC |= (1 << SCL) | (1 << SDA);//pull up
DDRC &= ~((1 << SCL) | (1 << SDA));//in
#if (F_CPU > 1000000UL)
TWBR = 0x10;
// 1e6/(16+2*TWBR*TWPS) < 100 kHz = 1e5
// 8e6/(16+2*0x20*1) = 100 kHz = 1e5
//TWSR |= (0 << TWPS1) | (0 << TWPS0); //prescaler 1
//TWBR = 0x80;
#endif
//TWAR = //address & general call
//_delay_ms(100);
//write_one(CTRL_REG1, );
write_one(s, CTRL_REG1, ((0 << PD) | (0b001 << ODR)));//poweroff set odr
//write_one(CTRL_REG2, (1 << BOOT));
write_one(s, CTRL_REG1, ((1 << PD) | (1 << BDU) | (0b001 << ODR)));
/*while(1){
//write_one(CTRL_REG1, ((1 << PD) | (1 << BDU) | (0b000 << ODR))); // wake up
//read(WHO_AM_I, 1);
//write_one(CTRL_REG2, (1 << ONE_SHOT)); //set one shot meas
//while(( read(STATUS_REG, 1) & ( (1 << P_DA) | (1 << T_DA) ) ) != ( (1 << P_DA) | (1 << T_DA)) ); //wait meas
read(STATUS_REG, 1);
p = read(PRESS_OUT, 3); //read pout
t = read(TEMP_OUT, 2);
_delay_ms(1);
}*/
}
psens psens_create(uint8_t addr){
psens s = (psens) malloc(sizeof(struct psens_struct));
if (s != NULL){
//s->scl = scl; can not change
//s->sda = sda;
s->addr = addr;
psens_init(s);
}
return s;
}
void psens_destroy(psens s){
free(s);
}
float psens_gettemp(psens s){
return (float) ((int16_t)read(s, TEMP_OUT, 2)) / 480.0 + 42.5;
}
float psens_getpressure(psens s){
return ((float) read(s, PRESS_OUT, 3)) / 4096.0 ;
}
int8_t S_SLA_W(psens s){
// ST send start bit
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // | (1 << TWIE);
TW_WAIT();
if((TWSR & MASK) != TW_START){ // chk status
TWCR = (0 << TWEN); //reset
return -1; //error
}
// SLA+W
TWDR = (s->addr << 1) + W;
TWCR = (1 << TWINT) | (1 << TWEN);//return
TW_WAIT();
if((TWSR & MASK) == TW_MT_SLA_NACK){
TW_STOP();
return -1;
}else if((TWSR & MASK) != TW_MT_SLA_ACK){ //chk
return -1;//error
}
return 0;
}
int8_t SR_SLA_R(psens s){
// SR: restart
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
TW_WAIT();
if((TWSR & MASK) != TW_REP_START){ // chk status
TWCR = (0 << TWEN); //reset
return -1; //error
}
// SLA + R
TWDR = (s->addr << 1) + R;
TWCR = (1 << TWINT) | (1 << TWEN);//return
TW_WAIT();
if((TWSR & MASK) == TW_MR_SLA_NACK){ //chk
TW_STOP();
return -1;//error
}else if((TWSR & MASK) != TW_MR_SLA_ACK){ //chk
return -1;//error
}
return 0;
}
int32_t read(psens s, uint8_t reg_add, uint8_t bits){
int32_t res = 0;
if(bits > 3){ return -1;}
if(S_SLA_W(s) == -1){
return -1;
}
// SUB: multiread? + register add
TWDR = reg_add + (((bits > 1)?1:0) << 7); //single read MSB = 0;
TWCR = (1 << TWINT) | (1 << TWEN);
TW_WAIT();
if((TWSR & MASK) != TW_MT_DATA_ACK){ //chk
return -1;//error
}
if(SR_SLA_R(s) == -1){
return -1;
}
// DATA read
for(uint8_t i = 0; i < bits; i++){
if(i == (bits-1)){ //is next data last?
TWCR = (1 << TWINT) | (1 << TWEN) | (0 << TWEA); //NACK
TW_WAIT();
if((TWSR & MASK) != TW_MR_DATA_NACK){ //chk
return -1;//error
}
}else{
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA); //ACK
TW_WAIT();
if((TWSR & MASK) != TW_MR_DATA_ACK){ //chk
return -1;//error
}
}
res += ((unsigned long int)TWDR << (8*i));
//res = TWDR * 2(8*2);
}
// SP
TWCR = (1 << TWINT) | (1 << TWSTO)| (1 << TWEN);
return res;
//return 0x3F6991;
}
//
// write
//
int8_t write_one(psens s, uint8_t reg_add, uint8_t dat){
if(S_SLA_W(s) == -1){
return -1;
}
// SUB: multiwrite? + register add
TWDR = reg_add + (0 << 7); //single read MSB = 0;
TWCR = (1 << TWINT) | (1 << TWEN);
TW_WAIT();
if((TWSR & MASK) != TW_MT_DATA_ACK){ //chk
return -1;//error
}
// DATA write
TWDR = dat;
TWCR = (1 << TWINT) | (1 << TWEN);
TW_WAIT();
if((TWSR & MASK) == TW_MT_DATA_NACK){ //chk
TW_STOP();
return -1;//error
}else if((TWSR & MASK) != TW_MT_DATA_ACK){ //chk
return -1;//error
}
// SP
TW_STOP();
return 0;
}
lcd.c
#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
#include "lcd.h"
/* optlex dmc16117a
4bits mode
pin
1 Vss gnd
2 Vcc power for logic
3 Vee power for lcd -> contrast ~ 4.5V
4 RS register select, 0: instruction/address reg., 1: data reg.
5 RW read/write select, 0: write, 1: read
6 E enable signal, 0->1:start
11 db4
12 db5
13 db6
14 db7
*/
#define RS PINB0
#define RW PINB1
#define EN PINB2
#define DB4 PINB4
#define DB5 PINB5
#define DB6 PINB6
#define DB7 PINB7
#define DAT 0b11110000 // depends on pins
#define CTL 0b00000111 //
#define CLEAR_DISP 0x01
//#define RETURN_HOME 0x02
#define DISP_OFF 0x08
#define DISP_ON 0x0c
#define CURSOR 0x02
#define BRINK 0x01
#define FUNC_SET 0x20
//#define FOUR_BIT 0x00
#define TWO_LINE 0x08
#define ONE_LINE 0x00
#define SET_ADDR 0x80
#define BUSY (1 << PINB7)
//#define ADDR 0x7F
#define OUT 0xff
#define IN 0x00
struct lcd_struct{
};
uint8_t is_busy(void){
DDRB = (IN & DAT) | (OUT & CTL) ;//read from DAT
PORTB = (_BV(RW) & CTL);//EN,RS:0, RW:1, dat pull d;
PORTB |= _BV(EN);//EN:1
_delay_us(1);//220 ns
uint8_t busy = PINB & BUSY;
PORTB &= ~_BV(EN);//EN:0
//discard lower 4 bits
PORTB |= _BV(EN);//EN:1
_delay_us(1);//220 ns
PORTB &= ~_BV(EN);//EN:0
return busy;
}
uint8_t write_dat(uint8_t c){
while(is_busy());
DDRB = (OUT & DAT) | (OUT & CTL);//all output
PORTB = (_BV(RS) & CTL) | (((c >> 4) << DB4) & DAT);//EN,RW 0, RS:1, dat upper4
PORTB |= _BV(EN);//EN 1 (RS,RW send)
PORTB &= ~_BV(EN);//EN 0 (DAT send)
PORTB = (_BV(RS) & CTL) | (((c & 0x0f) << DB4) & DAT);//EN,RW:0, RS:1, dat lower4
PORTB |= _BV(EN);//EN 1
PORTB &= ~_BV(EN);//EN 0
// wait busy flag and t_add
//_delay_us(37+4);
return 0;
}
void write_cmd4b(uint8_t c){
//no wait
DDRB = DAT | CTL;//all output
PORTB = ((c & 0x0f) << DB4) & DAT;//EN,RW:0, RS:0, dat only lower4
PORTB |= _BV(EN);//EN 1
PORTB &= ~_BV(EN);//EN 0
PORTB |= _BV(EN);//EN 1
PORTB &= ~_BV(EN);//EN 0
}
void write_cmd8b(uint8_t c){
while(is_busy());
DDRB = DAT | CTL;//all output
PORTB = (((c >> 4) << DB4) & DAT);//EN,RW:0, RS:0, dat upper4
PORTB |= _BV(EN);//EN 1
PORTB &= ~_BV(EN);//EN 0
PORTB = (((c & 0x0f) << DB4) & DAT);//EN,RW:0, RS:0, dat lower4
PORTB |= _BV(EN);//EN 1
PORTB &= ~_BV(EN);//EN 0
//_delay_us(37);
}
void pwm_init(uint8_t cont){
DDRD = (1 << PORTD3);
OCR2A = cont; //0x60;//TOP value
OCR2B = (OCR2A >> 1);//duty 50%
TCCR2A = 0b00100011;//OC2B PWM low-high, WGM = 111
TCCR2B = 0b00001001;//timer counter cont. reg., prescaler 0
}
void lcd_init(lcd l){
//pwm ~83 kHz -> negative charge pump -1.7V -> contrast pin
pwm_init(0x60);
_delay_ms(40);//after vcc2.7V:40ms, after vcc4.5v:15ms
write_cmd4b(0x03);
_delay_ms(5);//4.1ms
write_cmd4b(0x03);
_delay_us(100);//100us
write_cmd4b(0x03);
_delay_us(37);
write_cmd4b(0x02);//set 4bit interface
write_cmd8b(FUNC_SET|TWO_LINE);//funcset 2line , 5x8dot
// write_cmd8b(0x20);//1line , 5x8dot
write_cmd8b(DISP_OFF);
write_cmd8b(CLEAR_DISP);
write_cmd8b(0x06);//ent_mode_set, cursor inc on , disp shift off
write_cmd8b(DISP_ON);
}
void lcd_write_str(lcd l, char *str){
while(*str != '\0'){
write_dat(*str);
str++;
}
}
void lcd_home_pos(lcd l){
write_cmd8b(CURSOR);
}
void lcd_second_pos(lcd l){
write_cmd8b(SET_ADDR|0x40);
}
lcd lcd_create(void){
lcd l = (lcd) malloc(sizeof(struct lcd_struct));
if (l != NULL){
lcd_init(l);
}
return l;
}
void lcd_destroy(lcd l){
free(l);
}
lcd.h
/*
* lcd.h
*/
#ifndef __LCD_H__
#define __LCD_H__
/* PORTB are used*/
typedef struct lcd_struct* lcd;
lcd lcd_create(void);
void lcd_destroy(lcd l);
void lcd_write_str(lcd l, char *str);
void lcd_home_pos(lcd l);
void lcd_second_pos(lcd l);
#endif
Makefileは
PRINTF_LIB = $(PRINTF_LIB_FLOAT)
をコメントアウトします。