12.リアルタイムクロックRTC
12.1.リアルタイムクロックによる時刻表示
プロジェクト名:RtcPcf8523
プロジェクト概要
AdafruitのPCF8523RTCモジュールを使用した時計プロジェクトです。RTCとの通信はI2Cでアドレス0x68固定です。PicoはRTCが内蔵されていますが、このPCF8523RTCモジュールでは電池でバックアップされています。時刻はソースリストの以下関数内で設定します。
pcf820_write_current_time();
この関数はGPIO10ピンに接続されたタクトスイッチが、電源オン時、押されていれば実行され、時刻設定モードになります。通常はRTC内にバックアップされている時刻でスタートします。
PCF8523RTCにはスタンバイモードがあります。チップが主電源 VDD からすでに供給されており、バックアップ電池が接続されている場合は、スタンバイ モードに入ることができます。また、バッテリスイッチオーバーファンクションが初期状態では有効になっていないため、電源管理制御ビットで有効にする必要があります。
//battery switch-over function is enabled in standard mode;
//battery low detection function is enabled
buf[0] = 0x02;
buf[1] = 0x00;
i2c_write_blocking(I2C_PORT, RTC_ADDR, buf, 2, false);
部品リスト
Adafruit PCF8523搭載 RTCモジュール 1 スイッチサイエンス
GROVEの16 x 2 LCD 1 スイッチサイエンス
タクトスイッチ 1
配線図

ソースリスト
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"
//#define PICO_DEFAULT_LED_PIN 25
#define LED_PIN PICO_DEFAULT_LED_PIN
#define BUTTON_PIN 10
#define I2C_PORT i2c0
#define I2C_SDA 8
#define I2C_SCL 9
//10.2章参照
int WaitTerminalStartup()
float ReadAdcTemperature()
//---------------
//10.1章lcd関数参照
//Rtc ----------------------------------
#define RTC_ADDR 0x68
char DaysWeek[7][3] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
typedef struct sDateTime {
int year;
int month;
int day;
int weekIndex;
char week[3];
int hour;
int minute;
int sec;
} DateTime;
static void pcf8520_reset() {
uint8_t buf[] = {0x00, 0x58};
i2c_write_blocking(I2C_PORT, RTC_ADDR, buf, 2, false);
//battery switch-over function is enabled in standard mode;
//battery low detection function is enabled
buf[0] = 0x02;
buf[1] = 0x00;
i2c_write_blocking(I2C_PORT, RTC_ADDR, buf, 2, false);
}
static void pcf820_write_current_time(DateTime *dt) {
uint8_t buf[2];
uint8_t curdt[7];
curdt[0] = ((dt->sec/10) << 4) | (dt->sec%10);
curdt[1] = ((dt->minute/10) << 4) | (dt->minute%10);
curdt[2] = ((dt->hour/10) << 4) | (dt->hour%10);
curdt[3] = ((dt->day/10) << 4) | (dt->day%10);
curdt[4] = dt->weekIndex;
curdt[5] = ((dt->month/10) << 4) | (dt->month%10);
curdt[6] = (((dt->year - 2000)/10) << 4) | ((dt->year - 2000)%10);
for (int i = 3; i < 10; ++i) {
buf[0] = i;
buf[1] = curdt[i - 3];
i2c_write_blocking(I2C_PORT, RTC_ADDR, buf, 2, false);
}
}
static void pcf8520_read_raw(uint8_t *buffer) {
uint8_t val = 0x03;
i2c_write_blocking(I2C_PORT, RTC_ADDR, &val, 1, true); // true to keep master control of bus
i2c_read_blocking(I2C_PORT, RTC_ADDR, buffer, 7, false);
}
void pcf8520_convert_time(DateTime *dt, const uint8_t raw_time[7]) {
// Convert raw data into time
dt->sec = (10 * (int) ((raw_time[0] & 0x70) >> 4)) + ((int) (raw_time[0] & 0x0F));
dt->minute = (10 * (int) ((raw_time[1] & 0x70) >> 4)) + ((int) (raw_time[1] & 0x0F));
dt->hour = (10 * (int) ((raw_time[2] & 0x30) >> 4)) + ((int) (raw_time[2] & 0x0F));
dt->day = (10 * (int) ((raw_time[3] & 0x30) >> 4)) + ((int) (raw_time[3] & 0x0F));
dt->weekIndex = (int) (raw_time[4] & 0x07);
dt->week[0] = DaysWeek[raw_time[4]][0];
dt->week[1] = DaysWeek[raw_time[4]][1];
dt->week[2] = DaysWeek[raw_time[4]][2];
dt->month = (10 * (int) ((raw_time[5] & 0x10) >> 4)) + ((int) (raw_time[5] & 0x0F));
dt->year = 2000 + (10 * (int) ((raw_time[6] & 0xF0) >> 4)) + ((int) (raw_time[6] & 0x0F));
}
//main ------------------------------------------------
int main()
{
stdio_init_all();
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
gpio_init(BUTTON_PIN);
gpio_set_dir(BUTTON_PIN, GPIO_IN);
gpio_pull_down(BUTTON_PIN);
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();
pcf8520_reset();
DateTime dt;
if(gpio_get(BUTTON_PIN)){
dt.year = 2023;
dt.month = 5;
dt.day = 12;
dt.weekIndex = 5;
dt.hour = 13;
dt.minute = 10;
dt.sec = 00;
pcf820_write_current_time(&dt);
}
char buf[32];
uint8_t raw_time[7];
int real_time[7];
lcd_clear();
while (true) {
pcf8520_read_raw(raw_time);
pcf8520_convert_time(&dt, raw_time);
sprintf(buf, "%04d/%02d/%02d %c%c%c", dt.year, dt.month, dt.day, dt.week[0],dt.week[1],dt.week[2]);
lcd_set_cursor(0, 0);
lcd_string(buf);
sprintf(buf, " %02d:%02d:%02d", dt.hour, dt.minute, dt.sec);
lcd_set_cursor(1, 0);
lcd_string(buf);
printf("%04d/%02d/%02d %c%c%c", dt.year, dt.month, dt.day, dt.week[0],dt.week[1],dt.week[2]);
printf(" %02d:%02d:%02d\n", dt.hour, dt.minute, dt.sec);
sleep_ms(500);
}
return 0;
}
コメントをお書きください