大気圧をキャラクタLCDに表示しました



無事、大気圧をキャラクタ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)

をコメントアウトします。