· 

14.温度センサープロジェクト(4)MCP9808 ーRaspberry Pi Pico WindowsC言語入門

13.4.MCP9808による温度計測

プロジェクト名:TempMCP9808

プロジェクト概要

MCP9808を使用した高精度温度計測プロジェクトです。Picoとの通信はI2Cで、Adafruitのブレイクアウトボードではアドレスは0x18~0x1Fから選べます。初期値はアドレスは0x18です。

調整可能なアドレス ピンを備えた 1 つの I2C バス上に最大 8個可能

-40°C125°C の範囲で標準精度 0.25°C (-20°C100°C で最大 0.5°C を保証)

0.0625℃の分解能

2.7V5.5Vの電源およびロジック電圧範囲

動作電流: 200 μA (標準)

 

部品リスト

MCP9808HighAccuracyI2CTemperature Senso                  スイッチサイエンス

GROVE16 x 2 LCD                                                                  スイッチサイエンス

 

配線図

ソースリスト

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include "pico/stdlib.h"

#include "hardware/i2c.h"

#include "hardware/rtc.h"

#include "hardware/adc.h"

 

//#define PICO_DEFAULT_LED_PIN 25

#define LED_PIN PICO_DEFAULT_LED_PIN

#define I2C_PORT i2c0

#define I2C_SDA 8

#define I2C_SCL 9

 

//ADc

const float ConversionFactor = 3.3f / (1 << 12);

 

int WaitTerminalStartup(int timeout_msec) {

    int btnin;

    uint32_t st = time_us_32();

    while(true)

    {

        if(stdio_usb_connected())

        {

            return 0;

        }  

        if(timeout_msec != 0){

            uint32_t cur = time_us_32();

            if((cur - st) > (timeout_msec*1000)) {

                return 0;

            }

        }

        gpio_put(LED_PIN, 1);

        sleep_ms(200);

        gpio_put(LED_PIN, 0);

        sleep_ms(200);

    }

}

void ScanI2CBus() {

    printf("\nI2C Bus Scan\n");

    printf("   0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F\n");

    for (int addr = 0; addr < (1 << 7); ++addr) {

        if (addr % 16 == 0) {

            printf("%02x ", addr);

        }

        int ret;

        uint8_t rxdata;

        ret = i2c_read_blocking(I2C_PORT, addr, &rxdata, 1, false);

        sleep_ms(10);

 

        printf(ret < 0 ? "-" : "*");

        printf(addr % 16 == 15 ? "\n" : "  ");

    }

}

 

void InitAdc()

{

    adc_init();

    adc_set_temp_sensor_enabled(true);

}

 

float ReadOnBoardTemperature()

{

    adc_select_input(4);

    float tempV = (float)adc_read() * ConversionFactor;

    float tempC = 27.0f - (tempV - 0.706f) / 0.001721f;

    return tempC;

}

 

void InitRtc()

{

    char datetime_buf[256];

    char *datetime_str = &datetime_buf[0];

 

    //Start on 2023/5/9 Tuesday 10:24:00

    datetime_t t = {

            .year  = 2023,

            .month = 5,

            .day   = 9,

            .dotw  = 2, // 0:Sunday - 5:Friday

            .hour  = 10,

            .min   = 24,

            .sec   = 00

    };

 

    rtc_init();

    rtc_set_datetime(&t);

    sleep_ms(1);

}

 

//MCP9808 --------------------------------

#define MCP9808_ADDR 0x18

 

//hardware registers

const uint8_t REG_POINTER = 0x00;

const uint8_t REG_CONFIG = 0x01;

const uint8_t REG_TEMP_UPPER = 0x02;

const uint8_t REG_TEMP_LOWER = 0x03;

const uint8_t REG_TEMP_CRIT = 0x04;

const uint8_t REG_TEMP_AMB = 0x05;

const uint8_t REG_RESOLUTION = 0x08;

 

void mcp9808_check_limits(uint8_t upper_byte) {

 

    // Check flags and raise alerts accordingly

    if ((upper_byte & 0x40) == 0x40) { //TA > TUPPER

        printf("Temperature is above the upper temperature limit.\n");

    }

    if ((upper_byte & 0x20) == 0x20) { //TA < TLOWER

        printf("Temperature is below the lower temperature limit.\n");

    }

    if ((upper_byte & 0x80) == 0x80) { //TA > TCRIT

        printf("Temperature is above the critical temperature limit.\n");

    }

}

 

float mcp9808_convert_temp(uint8_t upper_byte, uint8_t lower_byte) {

    float temperature;

 

    //Check if TA <= 0°C and convert to denary accordingly

    if ((upper_byte & 0x10) == 0x10) {

        upper_byte = upper_byte & 0x0F;

        temperature = 256 - (((float) upper_byte * 16) + ((float) lower_byte / 16));

    } else {

        temperature = (((float) upper_byte * 16) + ((float) lower_byte / 16));

    }

    return temperature;

}

 

void mcp9808_set_limits() {

 

    //Set an upper limit of 30°C for the temperature

    uint8_t upper_temp_msb = 0x01;

    uint8_t upper_temp_lsb = 0xE0;

 

    //Set a lower limit of 20°C for the temperature

    uint8_t lower_temp_msb = 0x01;

    uint8_t lower_temp_lsb = 0x40;

 

    //Set a critical limit of 40°C for the temperature

    uint8_t crit_temp_msb = 0x02;

    uint8_t crit_temp_lsb = 0x80;

 

    uint8_t buf[3];

    buf[0] = REG_TEMP_UPPER;

    buf[1] = upper_temp_msb;

    buf[2] = upper_temp_lsb;

    i2c_write_blocking(I2C_PORT, MCP9808_ADDR, buf, 3, false);

 

    buf[0] = REG_TEMP_LOWER;

    buf[1] = lower_temp_msb;

    buf[2] = lower_temp_lsb;

    i2c_write_blocking(I2C_PORT, MCP9808_ADDR, buf, 3, false);

 

    buf[0] = REG_TEMP_CRIT;

    buf[1] = crit_temp_msb;

    buf[2] = crit_temp_lsb;;

    i2c_write_blocking(I2C_PORT, MCP9808_ADDR, buf, 3, false);

}

 

//lcd -------------------------------

#define LCD_ADDR 0x27

// commands

const int LCD_CLEARDISPLAY = 0x01;

const int LCD_RETURNHOME = 0x02;

const int LCD_ENTRYMODESET = 0x04;

const int LCD_DISPLAYCONTROL = 0x08;

const int LCD_CURSORSHIFT = 0x10;

const int LCD_FUNCTIONSET = 0x20;

const int LCD_SETCGRAMADDR = 0x40;

const int LCD_SETDDRAMADDR = 0x80;

 

//display entry mode

const int LCD_ENTRYSHIFTINCREMENT = 0x01;

const int LCD_ENTRYLEFT = 0x02;

 

//display and cursor control

const int LCD_BLINKON = 0x01;

const int LCD_CURSORON = 0x02;

const int LCD_DISPLAYON = 0x04;

 

//display and cursor shift

const int LCD_MOVERIGHT = 0x04;

const int LCD_DISPLAYMOVE = 0x08;

 

//function set

const int LCD_5x10DOTS = 0x04;

const int LCD_2LINE = 0x08;

const int LCD_8BITMODE = 0x10;

 

//backlight control

const int LCD_BACKLIGHT = 0x08;

const int LCD_ENABLE_BIT = 0x04;

 

#define LCD_CHARACTER  1

#define LCD_COMMAND    0

//#define MAX_LINES      2

//#define MAX_CHARS      16

#define MAX_LINES      2

#define MAX_CHARS      16

#define DELAY_US 600

 

void i2c_write_byte(uint8_t val) {

    i2c_write_blocking(I2C_PORT, LCD_ADDR, &val, 1, false);

}

void lcd_toggle_enable(uint8_t val) {

    sleep_us(DELAY_US);

    i2c_write_byte(val | LCD_ENABLE_BIT);

    sleep_us(DELAY_US);

    i2c_write_byte(val & ~LCD_ENABLE_BIT);

    sleep_us(DELAY_US);

}

void lcd_send_byte(uint8_t val, int mode) {

    uint8_t high = mode | (val & 0xF0) | LCD_BACKLIGHT;

    uint8_t low = mode | ((val << 4) & 0xF0) | LCD_BACKLIGHT;

 

    i2c_write_byte(high);

    lcd_toggle_enable(high);

    i2c_write_byte(low);

    lcd_toggle_enable(low);

}

void lcd_clear(void) {

    lcd_send_byte(LCD_CLEARDISPLAY, LCD_COMMAND);

}

void lcd_set_cursor(int line, int position) {

    int addr[4] = {0x80, 0xc0, 0x94, 0xd4};

    int val = addr[line] + position;

    lcd_send_byte(val, LCD_COMMAND);

}

static void inline lcd_char(char val) {

    lcd_send_byte(val, LCD_CHARACTER);

}

void lcd_string(const char *s) {

    while(true) {

        if(*s == 0) {

            break;

        }

        lcd_char(*s);

        s++;

    }

}

void lcd_init() {

    lcd_send_byte(0x03, LCD_COMMAND);

    lcd_send_byte(0x03, LCD_COMMAND);

    lcd_send_byte(0x03, LCD_COMMAND);

    lcd_send_byte(0x02, LCD_COMMAND);

 

    lcd_send_byte(LCD_ENTRYMODESET | LCD_ENTRYLEFT, LCD_COMMAND);

    lcd_send_byte(LCD_FUNCTIONSET | LCD_2LINE, LCD_COMMAND);

    lcd_send_byte(LCD_DISPLAYCONTROL | LCD_DISPLAYON, LCD_COMMAND);

    lcd_clear();

}

 

float ReadMcpTemperature()

{

    float temp;

    uint8_t buf[2];

    uint16_t upper_byte;

    uint16_t lower_byte;

    i2c_write_blocking(I2C_PORT, MCP9808_ADDR, &REG_TEMP_AMB, 1, true);

    i2c_read_blocking(I2C_PORT, MCP9808_ADDR, buf, 2, false);

 

    upper_byte = buf[0];

    lower_byte = buf[1];

    //isolates limit flags in upper byte

    mcp9808_check_limits(upper_byte & 0xE0);

    //clears flag bits in upper byte

    temp = mcp9808_convert_temp(upper_byte & 0x1F, lower_byte);

   

    return temp;

 

int main()

{

    stdio_init_all();

 

    gpio_init(LED_PIN);

    gpio_set_dir(LED_PIN, GPIO_OUT);

    gpio_put(LED_PIN, 0);

   

    i2c_init(I2C_PORT, 400*1000);

    gpio_set_function(I2C_SDA, GPIO_FUNC_I2C);

    gpio_set_function(I2C_SCL, GPIO_FUNC_I2C);

    gpio_pull_up(I2C_SDA);

    gpio_pull_up(I2C_SCL);

 

    WaitTerminalStartup(30*1000);

    printf("\nTerminal connected\n");

    ScanI2CBus();   

    printf("I2C Scan completed\n");

 

    lcd_init();

    InitAdc();

    InitRtc();

    mcp9808_set_limits();

      

    char buf[128];

    datetime_t nowdt;

    int presec = -1;

    while (1) {

        rtc_get_datetime(&nowdt);

        if(presec != nowdt.sec)

        {

            float bTemp = ReadOnBoardTemperature();

            float mcpTemp = ReadMcpTemperature();

 

            sprintf(buf, "%04d/%02d/%02d %02d:%02d:%02d", nowdt.year, nowdt.month, nowdt.day, nowdt.hour,nowdt.min, nowdt.sec);

            lcd_set_cursor(0, 0);

            lcd_string(buf);

 

                         //0123456789012345  

                         // MC:23.1 AD:12.5  

            sprintf(buf, " MC:%4.1f AD:%4.1f", mcpTemp, bTemp);

            lcd_set_cursor(1, 0);

            lcd_string(buf);

 

            printf("%4d/%02d/%02d %02d:%02d:%02d mcp:%4.1fC adc:%4.1fC\n",  nowdt.year, nowdt.month, nowdt.day,

                                                               nowdt.hour,nowdt.min, nowdt.sec, mcpTemp, bTemp);

        }

        presec = nowdt.sec;

        sleep_ms(200);

   }

   return 0;

 

}

Picoオンボード温度センサー値に比べて、4℃程度高く出ています。変動は少なく、安定しています。