東京オリンピックが終わり、しばらく放っておいたPIC16F1827 RTC 時計のプログラミングを再開しました。
一応動きましたので、公開します。(まだバグがあるかも知れません)
まず、回路図です。SETボタンを長押しすると時計セットモードになります。年、月、日、曜、時、分、秒の数値の変更は、UP/DOWNボタンで行います。SETモードでは、SETボタンを押すごとに年から秒まで変更できます。
MCCの設定です。まず、System moduleです。
内部クロック、FOSC=16MHzとしました。
ICSPを利用しますので、RA5は、RESET/MCLR/VPPとします。
SETボタン=RB2
UPボタン=RB3
DOWNボタン=RB5
として、inputとし、ウィークプルアップします。
プログラムです。
-----------------------------------------------------------------------------------------------------
/*
* PIC12F1827 MCC RTC TEST
* 2021.08.10
* JH7UBC Keiji Hata
*/
#include "mcc_generated_files/mcc.h"
#include "mcc_generated_files/examples/i2c1_master_example.h"
//LCD関係定義
#define I2C_LCD_addr 0x27
#define LCD_EN 0b00000100//Enable
#define LCD_BL 0b00001000//Back Light
#define LCD_CMD 0x00
#define LCD_CHR 0x01
#define LCD_LINE1 0x80
#define LCD_LINE2 0xC0
//RTC関係定義
#define RTC_addr 0x68
char *week[] = {" ","MON","TUE","WED","THU","FRI","SAT","SUN"};
uint8_t REG[8] = {0x00,0x00,0x00,0x01,0x01,0x01,0x20,0x10};//時刻データ初期値2020/01/01 MON 00:00:00
int OLD_REG[7] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};//前の時刻データを保存するレジスタ
int Disp_REG[7];//表示用レジスタ
uint8_t REG_addr = 0x00;
//時計セット関係
#define SET RB2
#define UP RB3
#define DOWN RB5
int Num = 6; //Clock data number 6:year 5:month 4:day 3:week 2:hour 1:mminute 0:second
int count = 0;
//割込み関係
volatile int IntFlag = 0;
//I2C LCDに1byteのdataを書き込む
void I2C_write_data(uint8_t data){
I2C1_Write1ByteRegister(I2C_LCD_addr, (data | LCD_EN | LCD_BL),(data | LCD_BL));
__delay_us(100);
}
//I2C LCDにコマンドまたは文字を送る
void LCD_write(uint8_t data, uint8_t mode){
//上位4bitを送る
I2C_write_data((data & 0xF0) | mode);
//下位4bitを送る
I2C_write_data(((data << 4) & 0xF0) | mode);
}
//I2C LCD 初期化
void LCD_init(){
__delay_ms(40);
LCD_write(0x33,LCD_CMD);//8bit mode set 2回
LCD_write(0x32,LCD_CMD);//8bit mode set 1回,4bit mode set
LCD_write(0x06,LCD_CMD);//Entry mode set
LCD_write(0x0C,LCD_CMD);//display ON,cursol OFF,blink OFF
LCD_write(0x28,LCD_CMD);//Function set 4bit mode,2line
LCD_write(0x01,LCD_CMD);//Clear display
__delay_ms(1);
}
void LCD_home(){
LCD_write(0x02,LCD_CMD);
__delay_ms(1);
}
void LCD_cursor(uint8_t x,uint8_t y){
if(y == 0){
LCD_write(LCD_LINE1 + x,LCD_CMD);
}
if(y == 1){
LCD_write(LCD_LINE2 + x,LCD_CMD);
}
}
//1文字表示
void putch(char ch){
LCD_write(ch,LCD_CHR);
}
//時刻データをRTCに書き込む
void Time_data_write(){
for(uint8_t i=0;i<8;i++){
I2C1_Write1ByteRegister(RTC_addr,i,REG[i]);
}
}
//RTCから時刻データを読みだし、REG[]に格納
void Time_data_read(){
I2C1_WriteNBytes(RTC_addr,®_addr,1);
I2C1_ReadNBytes(RTC_addr,®,7);
}
//REG[]のデータ(HEX)をDisp_REG[]のデータ(10進)に変換
void HEX2DEC(){
for(uint8_t i=0;i<=7;i++){
Disp_REG[i] = ((REG[i] & 0xF0)>>4)*10 + (REG[i] & 0x0F);
}
}
//Disp_REG[]のデータ(10進)をREG[]のデータ(HEX)に変換
void DEC2HEX(){
for(uint8_t i=0;i<=7;i++){
REG[i] = ((Disp_REG[i] / 10) << 4) | (Disp_REG[i] % 10);
}
}
void LCD_print(uint8_t n){
printf("%02d",Disp_REG[n]);
OLD_REG[n]=Disp_REG[n];
}
//Timeデータの表示
void Time_disp(){
if(Disp_REG[6] != OLD_REG[6]){
LCD_cursor(2,0);
LCD_print(6);
}
if(Disp_REG[5] != OLD_REG[5]){
LCD_cursor(5,0);
LCD_print(5);
}
if(Disp_REG[4] != OLD_REG[4]){
LCD_cursor(8,0);
LCD_print(4);
}
if(Disp_REG[3] != OLD_REG[3]){
LCD_cursor(12,0);
printf(week[Disp_REG[3]]);
OLD_REG[3] = Disp_REG[3];
}
if(Disp_REG[2] != OLD_REG[2]){
LCD_cursor(4,1);
LCD_print(2);
}
if(Disp_REG[1] != OLD_REG[1]){
LCD_cursor(7,1);
LCD_print(1);
}
if(Disp_REG[0] != OLD_REG[0]){
LCD_cursor(10,1);
LCD_print(0);
}
}
void Time_UP_DOWN(uint8_t n){
if(UP == 0){
Disp_REG[n]++;
while(UP == 0){
__delay_ms(10);
}
}
if(DOWN == 0){
Disp_REG[n]--;
while(DOWN == 0){
__delay_ms(10);
}
}
}
void Year_set(){
LCD_cursor(0,1);
printf("Y");
while(SET == 1){
Time_UP_DOWN(6);
if(Disp_REG[6]>=100){
Disp_REG[6]=99;
}
if(Disp_REG[6]<0){
Disp_REG[6]=0;
}
Time_disp();
__delay_ms(10);
}
while(SET == 0){
}
OLD_REG[6] = Disp_REG[6];
Num--;
}
void Month_set(){
LCD_cursor(0,1);
printf("M");
while(SET == 1){
Time_UP_DOWN(5);
if(Disp_REG[5]>12){
Disp_REG[5]=1;
}
if(Disp_REG[5]<1){
Disp_REG[5]=12;
}
Time_disp();
__delay_ms(10);
}
while(SET == 0){
}
OLD_REG[5] = Disp_REG[5];
Num--;
}
void Day_set(){
LCD_cursor(0,1);
printf("D");
while(SET == 1){
Time_UP_DOWN(4);
if(Disp_REG[4]>31){
Disp_REG[4]=1;
}
if(Disp_REG[4]<1){
Disp_REG[4]=31;
}
Time_disp();
__delay_ms(10);
}
while(SET == 0){
}
OLD_REG[4] = Disp_REG[4];
Num--;
}
void Week_set(){
LCD_cursor(0,1);
printf("W");
while(SET == 1){
Time_UP_DOWN(3);
if(Disp_REG[3]>7){
Disp_REG[3]=1;
}
if(Disp_REG[3]<1){
Disp_REG[3]=7;
}
Time_disp();
__delay_ms(10);
}
while(SET == 0){
}
OLD_REG[3] = Disp_REG[3];
Num--;
}
void Hour_set(){
LCD_cursor(0,1);
printf("h");
while(SET == 1){
Time_UP_DOWN(2);
if(Disp_REG[2]>23){
Disp_REG[2]=0;
}
if(Disp_REG[2]<0){
Disp_REG[2]=23;
}
Time_disp();
__delay_ms(10);
}
while(SET == 0){
}
OLD_REG[2] = Disp_REG[2];
Num--;
}
void Minute_set(){
LCD_cursor(0,1);
printf("m");
while(SET == 1){
Time_UP_DOWN(1);
if(Disp_REG[1]>59){
Disp_REG[1]=0;
}
if(Disp_REG[1]<0){
Disp_REG[1]=59;
}
Time_disp();
__delay_ms(10);
}
while(SET == 0){
}
OLD_REG[1] = Disp_REG[1];
Num--;
}
void Second_set(){
LCD_cursor(0,1);
printf("s");
while(SET == 1){
Time_UP_DOWN(0);
if(Disp_REG[0]>59){
Disp_REG[0]=0;
}
if(Disp_REG[0]<0){
Disp_REG[0]=59;
}
Time_disp();
__delay_ms(10);
}
while(SET == 0){
}
OLD_REG[0] = Disp_REG[0];
Num--;
}
void Time_set(){
while(Num > -1){
switch(Num){
case 6:
Year_set();
break;
case 5:
Month_set();
break;
case 4:
Day_set();
break;
case 3:
Week_set();
break;
case 2:
Hour_set();
break;
case 1:
Minute_set();
break;
case 0:
Second_set();
break;
}
}
LCD_cursor(0,1);
printf(" ");
DEC2HEX();//Disp_REGデータ(10進)をREGデータ(HEX)に変換する
Time_data_write();//変更されたデータをRTCに書き込む
}
void main(){
// initialize the device
SYSTEM_Initialize();
// Enable the Global Interrupts
INTERRUPT_GlobalInterruptEnable();
// Enable the Peripheral Interrupts
INTERRUPT_PeripheralInterruptEnable();
LCD_init();
Time_data_write();//時刻をセット
//固定文字を表示
LCD_home();
printf("20 / / ( )");
LCD_cursor(6,1);
printf(": :");
//時刻表示
HEX2DEC();
Time_disp();
while (1){
if(IntFlag == 1){ //RB0にIOC割込みがあったら
Time_data_read();//RTCから時刻を読み出し
HEX2DEC(); //HEX→10進変換
Time_disp(); //時刻を表示する
IntFlag = 0; //割込みフラッグを降ろす
}
count = 0;
if(SET == 0){
while(SET == 0){
count++;
__delay_ms(5);
}
}
if(count>100){ //SETボタンが長押しされたら
INTCONbits.IOCIE = 0;//IOC割込み停止
INTCONbits.IOCIF = 0;//IOC割込みフラッグクリア
IntFlag = 0;
Time_set(); //時刻セットルーチンへ
Num = 6;
INTCONbits.IOCIE = 1;//IOC割込み再開
}
__delay_ms(10);
}
}
-----------------------------------------------------------------------------------------------------
ブレッドボードです。