/************************************************
STM32F103 によるOCXO Calibrator
2020/03/28より
V3.0の基板用
V4.0の基板用 STM_SSD1309_GPSFC_5V41.ino
(c)2020 jlb.jp
************************************************/
#include <Wire.h>
// I2C EEPROM アドレス
#define DEVICE_ADDRESS 0x50 //I2C EEPROM スレーブアドレス 24C256
#include <U8g2lib.h>
#include <SPI.h>
#include <wirish_time.h> // STM32
// #include <EEPROM.h> // STM32
#include "libmaple/timer.h" // STM32 GitHub より入手、
/*
timer.h ファイルは、
\Documents\ArduinoIDE1.8\hardware\Arduino_STM32\STM32F1\system\libmaple
\stm32f1\include\libmaple
(\libmaple フォルダは追加作成)
に保存した。
*/
// OLED SSD1309 Board Pin definitions [b4 AD9959 Signal]
#define SSD_SDA PA7 // Serial DATA Pin(out) SPI:MOSI [AD9959=IO0]
#define SSD_MISO PA6 // Serial DATA Pin(in) SPI:MISO [AD9959=IO2] not use
#define SSD_SCL PA5 // Serial DATA Pin(out) Low [AD9959=SCK]
#define SSD_CS PA4 // Chip Select Active Low(out) [AD9959=CSB]
#define SSD_RST PA3 // Reset(out) [AD9959=P3]
#define SSD_DC PA2 // Data/Command(out) [AD9959=P2]
//
U8G2_SSD1309_128X64_NONAME0_F_4W_HW_SPI u8g2(U8G2_R0, SSD_CS, SSD_DC, SSD_RST);
//
#define ADC_In PA0 // アナログ入力ピンの定義(予備) ■V2.2:V3.0■
#define SWa_In PA1 // コントロールSW アナログ入力ピンの定義 ■V2.2:V3.0■
#define coGSET PA8 // Counter Gate Active High(out)Low edge
#define ciGATE PA15 // Counter Gate ON ■V2.2:V3.0■
#define coPicREST PB3 // Counter reset Active High(out) ■V4■
#define coRSET PB4 // Counter reset Active LOW(out) ■V41■
#define LED_OUT PB8 // Counter Input 1/16 Active Low edge(increment)
#define ciPPS PB9 // Counter Input 1sec Active High edge(increment)
#define LED_PIN PC13 // PC13 or 32 or D32
// ==============================================
// 自作関数のプロトタイプ宣言
void handle_timer1(void);
void handle_timer2(void);
void handle_timer3(void);
void Opening_Text(void);
void func_print(uint8_t, const char*);
void Text_setCursor(uint8_t, uint8_t);
void drawTxline(uint8_t);
int mid(char, char, int, int);
int cmmSTRcmm(char, char, int);
int8_t extractGPGGA(char*);
int8_t extractGNRMC(char*);
int8_t extractGPZDA(char*);
uint8_t jobSelect(uint8_t); // F0キーにより、設定等の処理選択
int8_t buttonSW(void); // M ボタンの状態を返す
uint8_t confPRMset(uint8_t, uint8_t); // PRM set
uint64_t hex16Bto64bit(uint64_t *, char * );
void dFRQtoStr(char, double); // 周波数のフォーマット
void dFRQtoStrV(char, double); // 周波数のフォーマット
//uint8_t write(uint16_t, uint8_t);
//uint8_t read(uint16_t);
void RegZero(void); // カウンター用レジスタのゼロクリヤー
void PortReset(void); // ポート初期セット&ゲートF/F・PICカウンタ値のリセット
void PicReset(void); // PICのCPUリセット
void PpsSet(void); // スタート・リスタート時の PPS セット
// ==============================================
// ==============================================
// タイマー割り込み関係
volatile int32_t tIntCount1; // Timer1:割込み回数
volatile int32_t tIntCount2; // Timer1:割込み回数
volatile int32_t tIntCount3; // Timer3:割込み回数(int内で使用=volatile を付ける)
// ==============================================
// シリアル受信処理関係
uint8_t RXposi = 0;
uint8_t RCVok = 0;
uint8_t PICposi = 0;
uint8_t PICok = 0;
byte RXchar, PCchar, PICchar;
char RXbuff[256];
char RXbuff2[256];
char PICbuff[32]; // '[',16文字,SUMx2,']'PICからのカウンターデータ受取
char tmpbuff[64];
char useGPS[5];
char nowTime[22];
char nowDate[22];
char logdata[256];
char tmptmp[32];
// uint16_t SSt; // キー操作用変数
int8_t Keycnt = 0;
uint8_t FuncEnable = 0; // Function キー有効ビット F0:bit4, F1:bit3, F2:bit2, F3:bit1,
uint8_t jobPRM = 0; // 編集画面の処理No
uint8_t bpsPRM = 0; // BPSの指数
uint8_t hostPRM = 0; // HOSTへ送出するデータ選択 0:GPS Data, 1:LOG Data, 2:None
uint8_t gatePRM = 0; // GATE時間の指数
uint8_t dispNo = 0; // 表示番号 0:初期画面ならびに計測中, 1:各パラメータ表示中
uint8_t Counting = 0; // 計数中 = 1;
uint16_t gpsLoss = 0; // GPSエラー回数
uint16_t ppsLoss = 0; // PPS信号間隔のエラー回数
uint32_t CountTime = 0; // 計測回数
uint32_t picLoss = 0; // PICデータ受信エラー回数
// GPSとの通信速度変更用(チェックSUM(27:2桁)は、実測値) \r\n は、Serial.println で付加する。
const char bps96set[] = "$PMTK251,9600*17"; // (1秒中のデータ占有率 65.1%)(default)
const char bps192set[] = "$PMTK251,19200*22"; // (1秒中のデータ占有率 32.6%)
const char bps384set[] = "$PMTK251,38400*27"; // (1秒中のデータ占有率 16.3%)
const char bps576set[] = "$PMTK251,57600*2C"; // (1秒中のデータ占有率 10.9%)20cmで時々崩れ?
const char bps1152set[] = "$PMTK251,115200*1F"; // (1秒中のデータ占有率 5.4%)*使用しない*
const char hotStart[] = "$PMTK101*32"; // コールドスタート ANS="$PMTK809*33"
const char warmStart[] = "$PMTK102*31"; // ワームスタート ANS="$PMTK809*33"
const char coldStart[] = "$PMTK103*30"; // コールドスタート ANS="$PMTK605*31"
// カウンター用レジスタ?
volatile uint64_t ui64Reg = 0; // 符号無し(64bit) 周波数カウント用
//uint64_t ui64PicReg = 0;
//uint64_t ui64tmp;
uint8_t sumchk;
double dFrqReg = 0; // 浮動小数点(64bit) 周波数計算用
double dAvgReg = 0; // 浮動小数点(64bit) アベレージ計算用
double dMaxReg = 0; // 浮動小数点(64bit) 最高値保持
double dMinReg = 0; // 浮動小数点(64bit) 最小値保持
double dTmpReg = 0; // 浮動小数点(64bit) 汎用
double dP_PReg = 0; // 浮動小数点(64bit) p-p 振れ幅
int16_t AddCnt = 0; // 加算回数
char fmtFrq[22];
double B4dFrqReg = 0; // 浮動小数点(64bit) 周波数計算用
float IncDec = 0; // 増減計算用
// (int内で使用=volatile を付ける)
volatile int16_t ppsCnt = 0; // ゲートON時のPPSカウント用
uint8_t countDone = 0; // カウント完了
volatile uint8_t gateON = 0; // ゲートの状態
//uint8_t Running = 0; // カウント中
volatile uint8_t reStart = 0; // PPS信号が欠落したときの再開フラグ
char tmpsec[3] = "00"; // ■臨時■ 秒単位でのLOG送信に使用する。
// CSVのヘッダー(1行目)のデータ列(2020/03/20 17:00 現在)
// Num=連番, YYMMDD=年月日,HHMMSS=時分秒 ,GpsQ=QPS品質, Sat=使用衛星数,
// Freq=周波数, (+-)=前差, gpsLoss=GPS信号ロス回数, ppsLoss=PPS信号ロス回数
const char logheader[] = "Num,YYMMDD,HHMMSS,GpsQ,Sat,Freq,(+-),gpsLoss,ppsLoss,picLoss";
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// ◆◆◆◆◆電源投入時の初期画面◆◆◆◆◆
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
//
// □■■■□□■■■■□□■■■■■□■□□□■
// ■□□□■□■□□□■□■□□□□□■■□□■
// ■□□□■□■□□□■□■□□□□□■■□□■
// ■□□□■□■■■■□□■■■■□□■□■□■
// ■□□□■□■□□□□□■□□□□□■□□■■
// ■□□□■□■□□□□□■□□□□□■□□■■
// □■■■□□■□□□□□■■■■■□■□□□■
void Opening_Text()
{
u8g2.clearBuffer(); // clear
Text_setCursor(0, 1); // 桁・行
u8g2.print("OCXO Calibrator (GPS)");
Text_setCursor(1, 2); // 桁・行
u8g2.print("V41 & Ver.0.04.29");
Text_setCursor(0, 3); // 桁・行
u8g2.print(" (c) 2020, jlb.jp ");
FuncEnable = B00001111;
func_print(0, "MENU");
func_print(1, "GATE");
func_print(2, "STRT");
func_print(3, "CngD");
u8g2.sendBuffer(); // OLEDに送信(描画する)
}
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// カウンター用レジスタのゼロクリヤー
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
void RegZero() {
ui64Reg = 0;
dFrqReg = 0;
dAvgReg = 0;
dMaxReg = 0;
dMinReg = 0;
dTmpReg = 0;
dP_PReg = 0;
AddCnt = 0;
ppsCnt = 0;
gateON = 0;
gpsLoss = 0;
ppsLoss = 0;
tIntCount3 = 0;
//Running = 0; // 未使用?
B4dFrqReg = 0; // 浮動小数点(64bit) 周波数計算用
IncDec = 0; // 増減計算用
picLoss = 0; // PICデータ受信エラー回数
// 重要 入れないとPICからの古いデータが残る場合が有る。
while (Serial3.available()) {
PICchar = Serial3.read();
delay(2); // 9600bps時に1文字1.04mSsec
}
PICok = 0;
}
// GATE用D-F/Fのリセット V41時は反転(LOW アクティブ)
void PortReset() {
digitalWrite(coRSET, LOW);
delay(1); // 1mS
digitalWrite(coRSET, HIGH);
delay(1); // 1mS
}
// PICのリセット
void PicReset() {
digitalWrite(coPicREST, HIGH);
delay(1); // 1mS
digitalWrite(coPicREST, LOW);
delay(10); // 10mS
}
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// PPS 割り込みのカウント
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
void intPps() { // PPS 割込み
if (tIntCount3 >= 12) { // 前回のPPS信号から1.2秒以上経過している。
digitalWrite(coGSET, LOW);
gateON = 0; // GATEの状態OFF
reStart = 1; // PPS信号が欠落した後の再開フラグ SET
} else {
// 正常処理
if (digitalRead(coGSET) == HIGH) {
gateON = 1; // GATEの状態ON
// 最終手前のPPS(次のPPSでカウント完了)なら、GSETをOFFする。
if (ppsCnt <= 1) digitalWrite(coGSET, LOW);
ppsCnt--;
} else {
gateON = 0; // GATEの状態OFF
if (Counting == 1) countDone = 1; // カウント完了
ppsCnt = 0; // PPS Count Zero
// delay(1); // delay() は機能しない。
}
}
tIntCount3 = 0; // PPS信号の間隔を監視するカウンタ
}
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// PPS パラメータセット <<共通使用>>
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
void PpsSet() {
switch (gatePRM) {
case 1:
ppsCnt = 10; // 11秒/回
break;
case 2:
ppsCnt = 100; // 1分41秒/回
break;
case 3:
ppsCnt = 1000; // 16分41秒/回
break;
case 4:
ppsCnt = 10000; // 2時間46分41秒/回
break;
default:
ppsCnt = 1;
break;
}
}
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// double の数値を周波数表記に変換する
//
// 小数点以下の位をGATE時間に応じて可変(Variable)する(2020/04/15)
//
// 01234567890123456
// 98765432.1234 ----sprintf 後の状態
// 98 765 432.1234Hz ----書式変換後のイメージ
// 10MHz台まで表示する。桁の先頭がzeroの場合は空白。
// 3桁カンマは付けずに空白。少数以下4桁まで。
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
void dFRQtoStrV(char* fmttmp, double freqIN) {
double dtmp;
char strtmp[22];
int8_t i, j, ln;
dtmp = freqIN;
switch (gatePRM) {
case 1:
sprintf(strtmp, "%10.1lf", dtmp);
break;
case 2:
sprintf(strtmp, "%11.2lf", dtmp);
break;
case 3:
sprintf(strtmp, "%12.3lf", dtmp);
break;
case 4:
sprintf(strtmp, "%13.4lf", dtmp);
break;
default:
sprintf(strtmp, "%10.1lf", dtmp);
strtmp[9] = ' ';
break;
}
ln = strlen(strtmp) + 2;
j = 0;
for (i = 0; i < ln; i++) {
switch (i) {
case 2:
case 5:
fmttmp[j] = ' ';
j++;
fmttmp[j] = strtmp[i];
j++;
break;
default:
fmttmp[j] = strtmp[i];
j++;
break;
}
}
fmttmp[15] = 0x0;
}
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// double の数値を周波数表記に変換する
//
// アベレージの表示専用にする(2020/04/15)
//
// 01234567890123456
// 98765432.1234 ----sprintf 後の状態
// 98 765 432.1234Hz ----書式変換後のイメージ
// 10MHz台まで表示する。桁の先頭がzeroの場合は空白。
// 3桁カンマは付けずに空白。少数以下4桁まで。
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
void dFRQtoStr(char* fmttmp, double freqIN) {
double dtmp;
char strtmp[22];
int8_t i, j, ln;
dtmp = freqIN;
sprintf(strtmp, "%13.4lf", dtmp);
ln = strlen(strtmp) + 2;
j = 0;
for (i = 0; i < ln; i++) {
switch (i) {
case 2:
case 5:
fmttmp[j] = ' ';
j++;
fmttmp[j] = strtmp[i];
j++;
break;
default:
fmttmp[j] = strtmp[i];
j++;
break;
}
}
fmttmp[15] = 0x0;
}
/*************************************************************
受信した文字データ(SUM付)を uint64_t に変換し、SUM Check をする。
戻り値 uint64_t <== char inHex[20];
引数にSUM Checkの結果(sumFlag)を戻すため、アドレス渡しで関数を定義
使用時は、'&' を付ける。 hex16Bto64bit( &sumFlag, char *inHex)
sumFlag = 0;エラーなし
sumFlag = 1;エラー有り(発生はしない予想)
*************************************************************/
uint64_t hex16Bto64bit( uint8_t * sumFlag, char *inHex) {
int8_t i;
uint8_t tmpB1, tmpB2, tmpsum = 0;
uint64_t out64val = 0;
uint16_t sum = 0;
for (i = 15; i >= 0; i--) { // 文字配列の後から取り出す
tmpB1 = inHex[i];
sum += tmpB1;
if (tmpB1 > 0x39) {
tmpB2 = tmpB1 - 55;
tmpB2 &= 0x0F;
} else {
tmpB2 = tmpB1 & 0x0F;
}
out64val = out64val << 4;
out64val = out64val | tmpB2;
}
for (i = 17; i >= 16; i--) { // Check Sum
tmpB1 = inHex[i];
if (tmpB1 > 0x39) {
tmpB2 = tmpB1 - 55;
tmpB2 &= 0x0F;
} else {
tmpB2 = tmpB1 & 0x0F;
}
tmpsum <<= 4;
tmpsum |= tmpB2;
}
if (tmpsum == (uint8_t)sum) { // LSB側8bitを照合
*sumFlag = 0;
} else {
*sumFlag = 1;
}
return out64val;
}
////////////////////////////////////////////////////////////
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// ◆◆◆◆◆タイマー割込み◆◆◆◆◆
// 0.1秒ごとに数を増分する
// OLED の表示サイクル用に使用する 0.01Sec(timer1)
// PPS信号が欠落したことの検出に使用する。 0.1Sec(timer3)
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
void handle_timer1() {
tIntCount1++;
if (tIntCount1 > 500) tIntCount1 = 0; // 500 x 0.01S = 5 Sec 以上はクリアー
}
void handle_timer2() {
tIntCount2++; // 1mSec毎にカウント
if (tIntCount2 > 5000) tIntCount2 = 0; // 5000 x 1mS = 5 Sec 以上はクリアー
}
void handle_timer3() {
if (gateON == 1) tIntCount3++; // GATEがONの間、0.1Sec毎にカウント
}
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
//
// □■■■□□■■■■■□■■■■■□■□□□■□■■■■□
// ■□□□■□■□□□□□□□■□□□■□□□■□■□□□■
// ■□□□□□■□□□□□□□■□□□■□□□■□■□□□■
// □■■■□□■■■■□□□□■□□□■□□□■□■■■■□
// □□□□■□■□□□□□□□■□□□■□□□■□■□□□□
// ■□□□■□■□□□□□□□■□□□■□□□■□■□□□□
// □■■■□□■■■■■□□□■□□□□■■■□□■□□□□
//
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
void setup(void) {
disableDebugPorts(); // JTAG_Debug を使わないことで、PA15,PB3,PB4を使えるようにする。
pinMode(ADC_In, INPUT_ANALOG);
pinMode(SWa_In, INPUT_ANALOG);
// pinMode(ciCOUNT_T, INPUT_PULLUP); // ■V2.2:追加■
pinMode(coGSET, OUTPUT);
pinMode(ciGATE, INPUT);
pinMode(coPicREST, OUTPUT); // PIC AllRESET
pinMode(coRSET, OUTPUT); // Device Regi RESET
// pinMode(ciCOUNT, INPUT);
// #define ciCOUNT_T PA1 // Counter Input Timer2(TIM2_CH2) ■V2.2:追加■
pinMode(ciPPS, INPUT);
pinMode(LED_PIN, OUTPUT);
pinMode(LED_OUT, OUTPUT);
digitalWrite(LED_PIN, HIGH);
digitalWrite(LED_OUT, HIGH);
digitalWrite(coGSET, LOW);
PortReset(); // GATE用D-F/Fの初期化
PicReset(); // PICの初期化
attachInterrupt(ciPPS, intPps, RISING); // PPS 割り込み関数の指定。RISING
// attachInterrupt(ctQ7, intPictest, FALLING); // 臨時テスト
RegZero(); // レジスターの初期化
SPI.begin();
u8g2.begin();
// u8g2.setFont(u8g2_font_6x12_tr); // 7bit 等幅、21文字、5行(行12dot)、●●これが良さそう●●
u8g2.setFont(u8g2_font_6x12_t_symbols); // ?記号を使用するために、こちらのフォントを使う。
u8g2.setFontMode(1); /* activate transparent font mode */
Wire.begin(); // I2C EEPROM用、他
Opening_Text(); // 初期画面を表示
// ■■■■■■■■■■■■■■■
// EEPROM読み出し・初期化
// ■■■■■■■■■■■■■■■
uint8_t tmp;
tmp = read(0); // ROMからの読み出し Address = 0
if (tmp >= 0 && tmp <= 2) { // パラメータが範囲内 0 〜 2
bpsPRM = tmp; // = PRMへ読み出し値セット
} else { // パラメータが範囲外 = ROM新品または、故障??
write(0, 0); // 0 = (default) を書込み
}
tmp = read(1); // ROMからの読み出し Address = 1
if (tmp >= 0 && tmp <= 2) {
hostPRM = tmp;
} else {
write(1, 0);
}
tmp = read(2); // ROMからの読み出し Address = 2
if (tmp >= 0 && tmp <= 2) {
gatePRM = tmp;
} else {
write(2, 0);
}
// ■■■■■■■■■■■■■■■
// ★★★タイマー割込み 1 の初期設定 START★★★▼▼▼▼▼▼ OLEDの表示サイクル用
Timer1.pause(); // タイマー停止
Timer1.setPrescaleFactor(7200); // 分周値set 72MHz ÷ 7200 = 10KHz
Timer1.setOverflow(100); // 10KHz x 100 = 0.01秒でオーバーフォロー割込み
// ------
Timer1.attachInterrupt( // 割り込みハンドラの登録
TIMER_UPDATE_INTERRUPT, // 条件は、カウンターオーバーフロー更新時
handle_timer1); // 呼び出す関数
// ------
Timer1.setCount(0); // カウンタを0に設定
Timer1.refresh(); // タイマ更新
Timer1.resume(); // タイマースタート
// --------------------------------------
// ★★★タイマー割込み 2 の初期設定 START★★★▼▼▼▼▼▼ (処理時間の計測に臨時使用)汎用
Timer2.pause(); // タイマー停止
Timer2.setPrescaleFactor(7200); // 分周値set 72MHz ÷ 7200 = 10KHz (uint32)
Timer2.setOverflow(10); // 10KHz x 10 = 1mSsecでオーバーフォロー割込み (uint32)
// ------
Timer2.attachInterrupt( // 割り込みハンドラの登録
TIMER_UPDATE_INTERRUPT, // 条件は、カウンターオーバーフロー更新時
handle_timer2); // 呼び出す関数
// ------
Timer2.setCount(0); // カウンタを0に設定
Timer2.refresh(); // タイマ更新
Timer2.resume(); // タイマー2スタート
// ★★★タイマー割込みの初期設定 END ★★★▲▲▲▲▲▲
// --------------------------------------
// --------------------------------------
// ★★★タイマー割込み 3 の初期設定 START★★★▼▼▼▼▼▼ PPS信号の欠落検知用
Timer3.pause(); // タイマー停止
Timer3.setPrescaleFactor(7200); // 分周値set 72MHz ÷ 7200 = 10KHz (uint32)
Timer3.setOverflow(1000); // 10KHz x 1000 = 0.1秒でオーバーフォロー割込み (uint16)
// ------
Timer3.attachInterrupt( // 割り込みハンドラの登録
TIMER_UPDATE_INTERRUPT, // 条件は、カウンターオーバーフロー更新時
handle_timer3); // 呼び出す関数
// ------
Timer3.setCount(0); // カウンタを0に設定
Timer3.refresh(); // タイマ更新
Timer3.resume(); // タイマー2スタート
// ★★★タイマー割込みの初期設定 END ★★★▲▲▲▲▲▲
// --------------------------------------
Serial.begin(9600); // USBシリアル(PC側) STM32は、速度指定しても最高速度で動く。
//while (!Serial); // Arduino UNO 書込み後のUSBシリアルの解放を待つ。
//while (!Serial.isConnected()) delay(10); // STM32 USB-シリアル用 isConnected()を使う。
// ここでROMからリードしたbpsをセット GNSS用
switch (bpsPRM) {
case 1:
Serial1.begin(19200); // Serial1(PA9 PA10)
break;
case 2:
Serial1.begin(38400); // Serial1(PA9 PA10)
break;
default:
Serial1.begin(9600); // Serial1(PA9 PA10)
break;
}
Serial3.begin(38400); // PIC側と合わせる
delay(3000);
}
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
//
// ■□□□□□□■■■□□□■■■□□■■■■□
// ■□□□□□■□□□■□■□□□■□■□□□■
// ■□□□□□■□□□■□■□□□■□■□□□■
// ■□□□□□■□□□■□■□□□■□■■■■□
// ■□□□□□■□□□■□■□□□■□■□□□□
// ■□□□□□■□□□■□■□□□■□■□□□□
// ■■■■■□□■■■□□□■■■□□■□□□□
//
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// Loop一回の処理時間(タイマーで実測:2020/04/17)
// スタンバイ時:Max16mS(1mS〜12mS)
// スタート時 :Max194mS
// 計測中 :Max16mS(1mS〜15mS)
// ストップ時 :Max239mS
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
void loop(void) {
// digitalWrite(LED_PIN, !digitalRead (ciPPS)); // LED_PIN を PPS入力に同期させる
digitalWrite(LED_OUT, !digitalRead (ciPPS)); // LED_PIN を PPS入力に同期させる
switch (buttonSW()) { // キーが押されているか確認。
// パラメータ編集メニューへ
case 10: // 長押し(20)F0 ===>短押し(10)へ変更(2020/04/16)
if ((FuncEnable & 0x08) > 0) { // 許可フラグ
jobPRM = jobSelect(jobPRM); // ★★★設定処理等の選択画面に行く
u8g2.clearBuffer(); // ★★★設定処理等の選択画面からの帰りにクリアー
Text_setCursor(1, 1); // 桁・行
u8g2.print(" Config MENU END ");
u8g2.sendBuffer(); // OLEDに送信(描画する)
delay(500);
}
break;
// GATE time切換
case 11: // 短押しF1、長押しは無効として処理。
if ((FuncEnable & 0x04) > 0) { // 許可フラグ
if (dispNo == 0) {
gatePRM++;
if (gatePRM > 3) gatePRM = 0; // GATE time を変更、通常0〜3
} else {
// CLEAR 第2画面
PicReset(); // PICをリセットする。(INT0割り込みで)
delay(1);
}
}
break;
// GATE time切換
case 21: // 長押しF1で、10,000secの設定
if ((FuncEnable & 0x04) > 0) { // 許可フラグ
if (dispNo == 0) {
gatePRM = 4;
}
}
break;
// 計測スタート・ストップ
case 12: // 短押しF2、長押しは無効として処理。
if ((FuncEnable & 0x02) > 0) { // 許可フラグ
if (Counting == 0) {
Counting = 1; // カウントスタート
RegZero();
PortReset();
PicReset();
// ゲート時間により、
PpsSet(); // gatePRMからppsCntへ時間をセット
digitalWrite(coGSET, HIGH);
} else {
digitalWrite(coGSET, LOW);
Counting = 0; // カウントストップ
CountTime = 0;
delay(50);
}
}
break;
// 表示画面の切替
case 13: // 短押しF3、長押しは無効として処理。
if ((FuncEnable & 0x01) > 0) { // 許可フラグ
if (dispNo == 0) {
dispNo = 1; // 2画面へ変更
} else {
dispNo = 0; // 1画面へ変更
}
}
break;
} // switch() end
// ■■■もし、PPS信号が欠落したときの再開フラグが立ったら。■■■
if (reStart != 0) {
ppsLoss++; // PPSのロス回数を+1
PortReset(); // GATE用D-F/Fのリセット
PpsSet(); // gatePRMからppsCntへ時間をセット
ui64Reg = 0; // クリヤー
reStart = 0;
digitalWrite(coGSET, HIGH);
}
// ■■■PPSが長時間停止したときのカウント処理停止■■■
if (tIntCount3 > 200000) { // タイマー割り込み 100mS * 20000 = 20000S(約33.3分) で
// カウント処理を停止する。(PPS信号が約33.3分到達しなかった)
digitalWrite(coGSET, LOW);
Counting = 0; // カウントストップ
gateON = 0;
tIntCount3 = 0;
}
// --------------------------------------
if (Serial1.available()) { // ■Serial1(PA9 PA10)からUSBシリアル(PC側)■
RXchar = Serial1.read();
switch (RXposi) {
case 0:
if (RXchar == '$')
{
RXbuff[RXposi] = RXchar;
RXposi++;
} else {
RXposi = 0;
RCVok = 0;
}
break;
case 255: // バッファ数 255 一杯なら吐き捨てる
RXposi = 0;
RCVok = 0;
break;
default:
if (RXchar == 0x0a) // エンドマークなら '*'又は <lf>0x0a
{
RXbuff[RXposi] = 0x00; // 文字配列に0x00を書き込む
RCVok = 1;
RXposi = 0;
} else {
RXbuff[RXposi] = RXchar; // それ以外なら RXbuff[] に取り込む
RXposi++;
}
}
if (hostPRM == 0) { // 0:GPSデータを受け付ける状態
Serial.write(RXchar);
}
}
// --------------------------------------
if (RCVok == 1)
{
Text_setCursor(1, 3); // 桁・行
u8g2.print(RXbuff);
if (strncmp(RXbuff, "$GPGGA", 6) == 0) {
strcpy(RXbuff2, RXbuff);
extractGPGGA(RXbuff2); // GPS数を抽出
}
if (strncmp(RXbuff, "$GPZDA", 6) == 0) {
strcpy(RXbuff2, RXbuff);
extractGPZDA(RXbuff2); // UTCと年月日を抽出してJSTに変換する
}
RCVok = 0;
}
// --------------------------------------
if (Serial.available()) { // ■USBシリアル(PC側)からSerial1(PA9 PA10)■
PCchar = Serial.read();
Serial1.write(PCchar);
}
// --------------------------------------
if (Serial3.available()) { // ■Serial3(PB11)からカウンターデータ受取■
PICchar = Serial3.read();
switch (PICposi) {
case 0:
if (PICchar == '[') // '['= START Mark
{
PICposi++;
} else {
PICposi = 0;
PICok = 0;
}
break;
case 19:
if (PICchar == ']') // 0から数えて19番目が ']'= END Mark なら正常
{
PICbuff[PICposi - 1] = 0x00;
PICok = 1;
}
PICposi = 0;
break;
default:
if (PICchar != ']') // ']'= END Mark 以外なら
{
PICbuff[PICposi - 1] = PICchar;
PICposi++;
} else {
PICposi = 0;
PICok = 0;
}
break;
}
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
if (hostPRM == 2) { // 2:PICデータをHOSTへ送信する状態■テスト■
Serial.write(PICchar);
}
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
}
// --------------------------------------
// ■■■■■■周波数カウントが完了した時の処理■■■■■■
// ■■■■■■周波数カウントが完了した時の処理■■■■■■
// ■■■■■■周波数カウントが完了した時の処理■■■■■■
// ■■■■■■周波数カウントが完了した時の処理■■■■■■
// ■■■■■■周波数カウントが完了した時の処理■■■■■■
// ■■■■■■周波数カウントが完了した時の処理■■■■■■
if (countDone == 1 && PICok == 1) {
CountTime++; // 計測回数
// (sumchk != 0) SUM Check Error
// 1〜2cm間の伝送路のため、発生可能性はゼロに近いと思われるため
// SUM Check Error 時の処理は記述せず。(2020/04/10)
// そのまま処理して、picLoss を加算する。
ui64Reg = hex16Bto64bit(&sumchk, PICbuff);
if (sumchk > 0) picLoss++; // もし発生したら、ログ等への引き渡しのみ
dFrqReg = (double)ui64Reg; // double に変換
ui64Reg = 0; // クリヤー
// ゲート時間により、
switch (gatePRM) {
case 1:
dFrqReg = dFrqReg / 10;
break;
case 2:
dFrqReg = dFrqReg / 100;
break;
case 3:
dFrqReg = dFrqReg / 1000;
break;
case 4: // 実験的な(通常は選択できない)扱い
dFrqReg = dFrqReg / 10000;
break;
default:
break;
}
// アベレージに加算
dAvgReg += dFrqReg;
AddCnt++;
// 最高値の更新
if (dMaxReg < dFrqReg) dMaxReg = dFrqReg;
// 最低値の更新
if (dMinReg != 0) {
if (dMinReg > dFrqReg) dMinReg = dFrqReg;
} else {
dMinReg = dFrqReg;
}
// 振れ幅 p-p の更新
dP_PReg = dMaxReg - dMinReg;
// ■■■■■■■■■■■■■■■■■■■■■■■■
// 一つ前の測定結果との誤差を求める。
// ■■■■■■■■■■■■■■■■■■■■■■■■
if (B4dFrqReg == 0) {
B4dFrqReg = dFrqReg;
IncDec = 0;
} else {
IncDec = dFrqReg - B4dFrqReg;
B4dFrqReg = dFrqReg;
}
PICok = 0; // Serial3からの読み込みもクリヤー
countDone = 0; // 周波数カウントが完了した時の処理■■完了■■
if (Counting == 1) { // カウント継続中なら
// ゲート時間により、
PpsSet(); // gatePRMからppsCntへ時間をセット
PortReset(); // GATE用D-F/Fのリセット
digitalWrite(coGSET, HIGH); // カウントの継続セット
}
// ■■■■■■■■■■■■■■■■■■■■■■■■
// カウント完了ごとに、(LOG指定なら)LOG出力
// ■■■■■■■■■■■■■■■■■■■■■■■■
if (hostPRM == 1) { // 1:LOGデータをHOSTへ送信する状態■テスト■
if (CountTime == 1 ) { // もし計測1回目なら
Serial.println(logheader); // <cr><lf>を付けてヘッダー出力
}
sprintf(tmptmp, "%d", CountTime);
strcpy(logdata, tmptmp);
strcat(logdata, ",");
strcat(logdata, nowDate);
strcat(logdata, ",");
strcat(logdata, nowTime);
strcat(logdata, ",");
strcat(logdata, useGPS);
strcat(logdata, ",");
switch (gatePRM) { // GATE時間に応じて小数点以下を増減する(2020/04/29)
case 1:
sprintf(tmptmp, "%10.1lf", dFrqReg);
break;
case 2:
sprintf(tmptmp, "%11.2lf", dFrqReg);
break;
case 3:
sprintf(tmptmp, "%12.3lf", dFrqReg);
break;
case 4:
sprintf(tmptmp, "%13.4lf", dFrqReg);
break;
default:
sprintf(tmptmp, "%8.0lf", dFrqReg);
break;
}
strcat(logdata, tmptmp);
strcat(logdata, ",");
// sprintf(tmptmp, "%+10.4f", IncDec); // + - 記号付き、少数以下3桁、計8桁
switch (gatePRM) { // GATE時間に応じて小数点以下を増減する(2020/04/29)
case 1:
sprintf(tmptmp, "%+8.1f", IncDec);
break;
case 2:
sprintf(tmptmp, "%+9.2f", IncDec);
break;
case 3:
sprintf(tmptmp, "%+10.3f", IncDec);
break;
case 4:
sprintf(tmptmp, "%+11.4f", IncDec);
break;
default:
sprintf(tmptmp, "%+6.0f", IncDec);
break;
}
strcat(logdata, tmptmp);
strcat(logdata, ",");
sprintf(tmptmp, "%d", gpsLoss);
strcat(logdata, tmptmp);
strcat(logdata, ",");
sprintf(tmptmp, "%d", ppsLoss);
strcat(logdata, tmptmp);
strcat(logdata, ",");
sprintf(tmptmp, "%d", picLoss); // PICデータのSUM Checkエラー回数 デバッグ用
strcat(logdata, tmptmp);
Serial.println(logdata); // <cr><lf>を付けて出力
}
} // ■■■■■■周波数カウントが完了処理 終了 ■■■■■■
// --------------------------------------
// ■■■■■■■■■■■■■■■■■■■■■■■■
// OLEDへの表示処理
// ■■■■■■■■■■■■■■■■■■■■■■■■
if (tIntCount1 >= 20) { // タイマー割り込み 10mS * 20 = 200mS 毎に表示を更新する。■■■■
// *100mS 毎でも遅れは無い模様。 memo->( digitalRead(ciGON) == LOW && )
tIntCount1 = 0;
u8g2.clearBuffer(); // clear the internal memory
if (dispNo == 0) { // 基本画面
Text_setCursor(0, 0); // 桁・行
u8g2.print("Frq:");
dFRQtoStrV(fmtFrq, dFrqReg); // 周波数データを表示形式に変換
Text_setCursor(4, 0); // 桁・行
u8g2.print(fmtFrq);
Text_setCursor(19, 0); // 桁・行
u8g2.print("Hz");
Text_setCursor(0, 1); // 桁・行
u8g2.print(useGPS);
Text_setCursor(4, 1); // 桁・行
u8g2.print(",g");
Text_setCursor(6, 1); // 桁・行
u8g2.print(gpsLoss);
u8g2.print(",p");
u8g2.print(ppsLoss); // PS信号が欠落したとき デバッグ用
Text_setCursor(13, 1); // 桁・行
u8g2.print(nowTime);
Text_setCursor(1, 2); // 桁・行
u8g2.print("GATE=");
Text_setCursor(6, 2); // 桁・行
switch (gatePRM) {
case 1:
u8g2.print("10s");
break;
case 2:
u8g2.print("100s");
break;
case 3:
u8g2.print("1000s");
break;
case 4: // 実験的な扱い(長押しで選択)
u8g2.print("10^4s"); // 表示桁数節約のため 10の4乗 表記
break;
default:
u8g2.print("1s");
break;
}
sprintf(tmptmp, "%5d", CountTime); // 計測回数(最小55時間連続でカウントできる桁数)
Text_setCursor(11, 2); // 桁・行
u8g2.print(tmptmp);
sprintf(tmptmp, "%5d", ppsCnt); // GATEがOFFするまでの pps回数
Text_setCursor(16, 2); // 桁・行
u8g2.print(tmptmp);
Text_setCursor(1, 3); // 桁・行
u8g2.print("SEND=");
Text_setCursor(6, 3); // 桁・行
switch (hostPRM) {
case 0:
u8g2.print("GPS");
break;
case 1:
u8g2.print("LOG");
break;
case 2:
u8g2.print("PIC");
break;
}
// 仮■■ 変更したパラメータを表示する。デバッグ用
char aa = 0x30 | jobPRM;
char bb = 0x30 | bpsPRM;
char cc = 0x30 | hostPRM;
char dd = 0x30 | gatePRM;
Text_setCursor(10, 3); // 桁・行
// u8g2.print(aa);
u8g2.print(bb);
u8g2.print(cc);
u8g2.print(dd);
// ************************************************************************
//sprintf(tmptmp, "%2d", tIntCount3); // PPS信号の間隔(0.1Sec単位)デバッグ用
//Text_setCursor(14, 3); // 桁・行
// u8g2.print(tmptmp);
// ************************************************************************
sprintf(tmptmp, "%2d", picLoss); // PICデータのシリアル受信エラー回数 デバッグ用
Text_setCursor(19, 3); // 桁・行
u8g2.print(tmptmp);
if (Counting == 0) { // 停止中
FuncEnable = B00001111;
func_print(0, "MENU");
func_print(1, "GATE");
func_print(2, "STRT");
func_print(3, "CngD");
} else { // カウント中
FuncEnable = B00000011;
func_print(2, "STOP");
func_print(3, "CngD");
}
} else { // 拡張 第2画面■■■■■■■■
//
Text_setCursor(0, 0); // 桁・行
u8g2.print("Ave:");
if (AddCnt > 0) {
dTmpReg = dAvgReg / (double)AddCnt;
dFRQtoStr(fmtFrq, dTmpReg); // 周波数データを表示形式に変換
Text_setCursor(4, 0); // 桁・行
u8g2.print(fmtFrq);
}
Text_setCursor(19, 0); // 桁・行
u8g2.print("Hz");
//
Text_setCursor(0, 1); // 桁・行
u8g2.print("Max:");
dFRQtoStrV(fmtFrq, dMaxReg); // 周波数データを表示形式に変換
Text_setCursor(4, 1); // 桁・行
u8g2.print(fmtFrq);
Text_setCursor(19, 1); // 桁・行
u8g2.print("Hz");
//
Text_setCursor(0, 2); // 桁・行
u8g2.print("Min:");
dFRQtoStrV(fmtFrq, dMinReg); // 周波数データを表示形式に変換
Text_setCursor(4, 2); // 桁・行
u8g2.print(fmtFrq);
Text_setCursor(19, 2); // 桁・行
u8g2.print("Hz");
//
Text_setCursor(0, 3); // 桁・行
u8g2.print("P-P:");
dFRQtoStrV(fmtFrq, dP_PReg); // 周波数データを表示形式に変換
Text_setCursor(4, 3); // 桁・行
u8g2.print(fmtFrq);
Text_setCursor(19, 3); // 桁・行
u8g2.print("Hz");
if (Counting == 0) { // 停止中
FuncEnable = B00000101;
func_print(1, "pCLR");
func_print(3, "CngD");
} else { // カウント中
FuncEnable = B00000001;
func_print(3, "CngD");
}
}
// ■■■■■■■■■■■■■■■■■■■■■■■■
u8g2.sendBuffer(); // OLEDに送信(描画する)
/////////////////////////////////////
}
} // Loop End
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// 「ファンクション」表示関数
// 表示器の最下行はファンクション表示エリアとして、4つのボタンに割り当てる。
// 一つのボタンは4文字で、左1文字分を空けてBox描画で反転表示する。
// fnmb : ボタン番号
// str : 表示文字(4文字以内)
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
void func_print(uint8_t fnmb, const char* str)
{
int8_t boxORframe = 1; // 0 = Box描画, 1 = フレーム描画 切替予定
int8_t len;
len = strlen(str);
if ((len == 0) || (len > 4)) return;
switch (fnmb) {
case 0:
u8g2.setDrawColor(1); // 反転用の処理のため
if (boxORframe == 0) u8g2.drawBox(5, 50, 27, 13); // Box描画
else
u8g2.drawFrame(5, 50, 27, 13); // フレーム描画
u8g2.setDrawColor(2); // color 2 set
Text_setCursor(1, 4); // 桁・行
u8g2.print(str);
// u8g2.drawLine(6, 63, 31, 63); // (x1, y1, x2, y2)
break;
case 1:
u8g2.setDrawColor(1); // 反転用の処理のため
if (boxORframe == 0) u8g2.drawBox(35, 50, 27, 13); // Box描画
else
u8g2.drawFrame(35, 50, 27, 13);
u8g2.setDrawColor(2); // color 2 set
Text_setCursor(6, 4); // 桁・行
u8g2.print(str);
// u8g2.drawLine(36, 63, 61, 63); // (x1, y1, x2, y2)
break;
case 2:
u8g2.setDrawColor(1); // 反転用の処理のため
if (boxORframe == 0) u8g2.drawBox(65, 50, 27, 13); // Box描画
else
u8g2.drawFrame(65, 50, 27, 13);
u8g2.setDrawColor(2); // color 2 set
Text_setCursor(11, 4); // 桁・行
u8g2.print(str);
// u8g2.drawLine(66, 63, 91, 63); // (x1, y1, x2, y2)
break;
case 3:
u8g2.setDrawColor(1); // 反転用の処理のため
if (boxORframe == 0) u8g2.drawBox(95, 50, 27, 13); // Box描画
else
u8g2.drawFrame(95, 50, 27, 13);
u8g2.setDrawColor(2); // color 2 set
Text_setCursor(16, 4); // 桁・行
u8g2.print(str);
// u8g2.drawLine(96, 63, 121, 63); // (x1, y1, x2, y2)
break;
}
}
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// 文字出力の位置を、文字単位で指定する。
// 左上が0カラムの0行(合計:21文字x5行)
// 対象とするフォント: u8g2.setFont(u8g2_font_6x12_mf);
// *1文字目の左1ドットは、空ける。
// cc : 桁 0から20
// ll : 行 0から4
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
void Text_setCursor(uint8_t cc, uint8_t ll) // cc:0 to 20, ll:0 to 4 Use
{
uint8_t cp, lp;
cp = cc * 6 + 1;
lp = (ll + 1) * 12 - 1;
u8g2.setCursor(cp, lp);
}
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// 上記同様にテキストの行を指定して反転させる。
// 行は、0から4の5行として指定する。
// 対象とするフォント: u8g2.setFont(u8g2_font_6x12_mf);
// *1文字目の左1ドットは、空ける。
// cc : 桁 0から20
// ll : 行 0から4
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
void drawTxline(uint8_t ll)
{
uint8_t l1;
l1 = ll * 12 + 1;
u8g2.drawBox(0, l1, 127, 12);
}
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// バイト単位で文字列を抽出するMid関数
// 128文字以内を制限しないため、int8_t ではなく int 型を使用する。
// buf[]:抽出先
// str[]:抽出される文字列
// pos :抽出開始位置
// len :文字数
// return は、抽出した文字数を返す。
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
int mid(char *buf, char *str, int pos, int len)
{
int i;
int j = 0;
for (i = 0; str[i] != '\0'; i++) { // 対象の文字列の文字数分を末尾まで順に
if (i >= pos && i < pos + len) { // 指定位置と長さ分を
buf[j] = str[i]; // buf[j]に格納
j++;
}
}
buf[j] = '\0'; //bufの末尾にNULL文字を格納
return strlen(buf);
}
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// int cmmSTRcmm(char buf[], char str[], int cnt)
// 128文字以内を制限しないため、int8_t ではなく int 型を使用する。
//
// ','で挟まれた文字列を抽出する。
//
// buf[] :抽出先文字列
// str[] :検索対象文字列
// cnt :何個目の ',' 以後の文字列以降を抽出するのか指定
// 注)指定個数+1以上の ',' が発見できない「不成立」
// return
// 0〜n:抽出した文字数
// -1 :不成立
//
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
int cmmSTRcmm(char *buf, char *str, int cnt)
{
int c, i, h = 0, p1 = 0, p2 = 0;
int j = 0;
c = strlen(str);
if (cnt >= c) return -1; // 文字列が探したい位置より短い (-1)
for (i = 0; c > i; i++)
{
if (str[i] == ',') {
h++;
if (h == cnt) p1 = i; // p1に',' cnt個目の位置セット
if (h == cnt + 1) p2 = i; // p2に',' cnt+1個目の位置セット
}
}
if (h < cnt + 1) return -1; // 指定個数+1以上の ',' が発見できない (-1)
if (p1 + 1 == p2) { // ','間に文字列ゼロ
buf[0] = 0x00;
return 0;
}
for (i = 1; (p1 + i) < p2 ; i++)
{
buf[j] = str[p1 + i];
j++;
}
buf[j] = 0x00;
return p2 - p1 - 1; // 変換した文字数を返す
}
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// GPSデータから時分秒と使用中のGPS数を抽出して
// useGPS,nowTime にセットする。
// 対象データ:$GPGGA
// $GPGGA,001251.000,3445.9102,N,13527.9952,E,1,13,0.72,36.5,M,34.2,M,,*50<CR><LF>
// 時分秒.000, 北緯, 東経,「GPSのクオリティ0=No,1=OK,2=DGPS」, 使用衛星数, ]ほか
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
int8_t extractGPGGA(char *str) {
int8_t ci = 0;
char fn[2] = "0";
char tmp[16];
char tmps[16];
// 使用GPS数を抽出
if (cmmSTRcmm(tmp, str, 6) > 0) {
switch (tmp[0]) {
case '2': // 0:受信不能 1:単独測位 2:DGPS
fn[0]++;
case '1':
fn[0]++;
strcpy(useGPS, fn);
ci = cmmSTRcmm(tmp, str, 7);
if (ci == 0) {
strcat(useGPS, ",00"); // ゼロ文字 "00"表記
}
if (ci == 1) {
strcat(useGPS, ",0"); // 1文字 先頭に"0”を付加
strncat(useGPS, tmp, 1);
}
if (ci >= 2) {
strcat(useGPS, ",");
strncat(useGPS, tmp, 2); // 先頭二桁のみ抽出
}
break;
default:
gpsLoss++;
strcpy(useGPS, "0,00"); // ゼロ文字 "00"表記
break;
}
}
return ci; // 使用GPSの桁数を返す。0-1-2
}
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// GPSデータから時分秒と年月日を抽出する。
// useGPS,nowTime nowDateにセットする。 使わない予定 ==> GPZDA
// 対象データ:$GNRMC
// $GNRMC,001252.000,A,3445.9100,N,13527.9957,E,0.11,275.89,040120,,,A*7C<CR><LF>
// 時分秒.000,ステータスA有効:V無効,緯度,経度,速度,方向,日月年,地磁気,,モード*チェックSUM
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
int8_t extractGNRMC(char *str) {
int8_t jhh, jdd, jmm, cdd, cmm, leapY;
int16_t jyy;
char fn[2] = "0";
char tmp[16];
char tmps[16];
cdd = 0;
// 時分秒を抽出
if (cmmSTRcmm(tmp, str, 1) >= 10) { // HHMMDD.000 十文字以上なら
mid(tmps, tmp, 0, 2); // 2桁時
jhh = atoi(tmps); // UTC時間を数字に
jhh = jhh + 9; // UTC +9H = JST
if (jhh >= 24) {
jhh = jhh - 24; // 24H以上なら24Hを引く
cdd = 1;
}
sprintf(tmps, "%02d", jhh); // 2文字に変換
strcpy(nowTime, tmps); // JST出力
strcat(nowTime, ":");
mid(tmps, tmp, 2, 2); // 2桁分
strcat(nowTime, tmps);
strcat(nowTime, ":");
mid(tmps, tmp, 4, 2); // 2桁秒
strcat(nowTime, tmps);
strcpy(tmpsec, tmps); // 秒を記憶する■■
} else {
return 0;
}
if (cmmSTRcmm(tmp, str, 9) == 6) { // DDMMYY 6文字なら
mid(tmps, tmp, 4, 2); // 2桁・年
jyy = atoi(tmps); // UTC年を数字に
jyy = jyy + 2000;
//////////// 閏年の判定スタート
if (jyy % 4 == 0) {
if (jyy % 100 == 0) {
if (jyy % 400 == 0) {
leapY = 1;
} else {
leapY = 0;
}
} else {
leapY = 1;
}
} else {
leapY = 0;
}
//////////// 閏年の判定エンド
mid(tmps, tmp, 0, 2); // 2桁・日
jdd = atoi(tmps); // UTC日を数字に
jdd = jdd + cdd; // 24Hのオーバーフロー加算
mid(tmps, tmp, 2, 2); // 2桁・月
jmm = atoi(tmps); // UTC日を数字に
//////////// 月の最終日の判定
switch (jmm) {
case 4:
case 6:
case 9:
case 11:
if (jdd > 30) {
jdd = 1;
cmm = 1;
} else {
cmm = 0;
}
break;
case 2:
if (leapY == 0) {
if (jdd > 28) {
jdd = 1;
cmm = 1;
} else {
cmm = 0;
}
} else {
if (jdd > 29) {
jdd = 1;
cmm = 1;
} else {
cmm = 0;
}
}
break;
default: // 1 3 5 7 8 10 12
if (jdd > 31) {
jdd = 1;
cmm = 1;
} else {
cmm = 0;
}
break;
}
//////////// 月の最終日の判定
jmm = jmm + cmm;
if (jmm > 12) {
jmm = 1;
jyy++; // 年を進める
}
sprintf(tmps, "%04d", jyy); // 4文字
strcpy(nowDate, tmps); // JST出力
strcat(nowDate, "/");
sprintf(tmps, "%02d", jmm); // 2文字
strcat(nowDate, tmps); // JST出力
strcat(nowDate, "/");
sprintf(tmps, "%02d", jdd); // 2文字
strcat(nowDate, tmps); // JST出力
return 1; // 正常(時分秒と年月日の両方)抽出時は、1を返す。
} else {
return 0;
}
}
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// GPSデータから時分秒と年月日を抽出する。
// useGPS,nowTime nowDateにセットする。
// 対象データ:$GPZDA
// $GPZDA,001251.000,04,01,2020,,*54<CR><LF>
// 協定世界時(UTC)時分秒.000, DD,MM,YYYY,,*チェックSUM
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
int8_t extractGPZDA(char *str) {
int8_t jhh, jdd, jmm, cdd, leapY;
int16_t jyy;
char fn[2] = "0";
char tmp[16];
char tmps[16];
cdd = 0;
// 時分秒を抽出
if (cmmSTRcmm(tmp, str, 1) >= 10) { // HHMMDD.000 十文字以上なら
mid(tmps, tmp, 0, 2); // 2桁時
jhh = atoi(tmps); // UTC時間を数字に
jhh = jhh + 9; // UTC +9H = JST
if (jhh >= 24) {
jhh = jhh - 24; // 24H以上なら24Hを引く
cdd = 1;
}
sprintf(tmps, "%02d", jhh); // 2文字に変換
strcpy(nowTime, tmps); // JST出力
strcat(nowTime, ":");
mid(tmps, tmp, 2, 2); // 2桁分
strcat(nowTime, tmps);
strcat(nowTime, ":");
mid(tmps, tmp, 4, 2); // 2桁秒
strcat(nowTime, tmps);
strcpy(tmpsec, tmps); // 秒を記憶する■■
} else {
return 0;
}
if (cmmSTRcmm(tmp, str, 4) == 4) { // DDMMYY 6文字なら
jyy = atoi(tmp); // UTC年を数字に
//////////// 閏年の判定スタート
if (jyy % 4 == 0) {
if (jyy % 100 == 0) {
if (jyy % 400 == 0) {
leapY = 1;
} else {
leapY = 0;
}
} else {
leapY = 1;
}
} else {
leapY = 0;
}
//////////// 閏年の判定エンド
if (cmmSTRcmm(tmp, str, 2) == 2) { // 2桁・日
jdd = atoi(tmp); // UTC日を数字に
jdd = jdd + cdd; // 24Hのオーバーフロー加算
} else {
return 0;
}
if (cmmSTRcmm(tmp, str, 3) == 2) { // 2桁・月
jmm = atoi(tmp); // UTC月を数字に
} else {
return 0;
}
//////////// 月の最終日の判定
switch (jmm) {
case 4:
case 6:
case 9:
case 11:
if (jdd > 30) {
jdd = 1;
jmm++;
}
break;
case 2:
if (leapY == 0) { // 閏年ではない
if (jdd > 28) {
jdd = 1;
jmm++;
}
} else { // 閏年である
if (jdd > 29) {
jdd = 1;
jmm++;
}
}
break;
default: // 1 3 5 7 8 10 12
if (jdd > 31) {
jdd = 1;
jmm++;
}
break;
}
//////////// 最終月の判定
if (jmm > 12) {
jmm = 1;
jyy++; // 年を進める
}
sprintf(tmps, "%04d", jyy); // 4文字
strcpy(nowDate, tmps); // JST出力
strcat(nowDate, "/");
sprintf(tmps, "%02d", jmm); // 2文字
strcat(nowDate, tmps); // JST出力
strcat(nowDate, "/");
sprintf(tmps, "%02d", jdd); // 2文字
strcat(nowDate, tmps); // JST出力
return 1; // 正常(時分秒と年月日の両方)抽出時は、1を返す。
} else {
return 0;
}
}
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// 4つのキーによるパラメータ設定・変更操作
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// ◆◆◆◆◆F0キーによる設定等の処理選択◆◆◆◆◆
uint8_t jobSelect(uint8_t def) // F0キーにより、設定等の処理選択
{
int8_t i, j, p, xl;
int8_t stateRet = 0;
const char titlMsg[] = "Config MENU";
const char* selMsg[] = { "0.UART bps Set",
"1.SEND to HOST",
"2.First GATE time"
}; // このアイテム(item)の有効数-1(行数-1)を次に記述。
const uint8_t item = 2;
uint8_t tmpprm;
j = def;
p = 0;
while (1)
{
u8g2.clearBuffer(); // OLEDクリヤー
u8g2.setDrawColor(1); // 反転用の処理のため
u8g2.drawBox(0, 2, 128, 12); // Box描画
u8g2.setDrawColor(2); // color 2 set
Text_setCursor(0, 0); // 桁・行
u8g2.print(titlMsg); // タイトルの行を表示
for (i = 0; i <= item; i++)
{
Text_setCursor(2, i + 1); // 桁・行
u8g2.print(selMsg[i]);
}
xl = (j + 2) * 12; // マーカー位置指定
u8g2.drawGlyph(5, xl , 0x25b6); // 0x21d2⇒ 0x25b6?
FuncEnable = B00001111;
func_print(0, "RETN");
func_print(1, " UP ");
func_print(2, "DOWN");
func_print(3, "ENTR");
u8g2.sendBuffer(); // OLEDに送信(描画する)
switch (buttonSW()) {
case 10:
stateRet = 1;
break;
case 11:
// case 21:
j--;
if (j < 0) j = item;
break;
case 12:
// case 22:
j++;
if (j > item) j = 0;
break;
case 13:
// case 23:
switch (j) {
case 0: // UART BPS set
tmpprm = bpsPRM;
bpsPRM = confPRMset(j, tmpprm);
switch (bpsPRM) { // 以前の状態に関係なく速度切換を実行する。(2020/01/28)
case 1:
Serial1.println(bps192set); // <cr><lf>を付けて速度をGPSに指示
delay(50);
Serial1.println(bps192set); // <cr><lf>を付けて速度をGPSに指示(2回目)
delay(50);
Serial1.end(); // シリアルを一度止める
delay(50);
Serial1.begin(19200); // Serial1(PA9 PA10) オープン
break;
case 2:
Serial1.println(bps384set); // <cr><lf>を付けて速度をGPSに指示
delay(50);
Serial1.println(bps384set); // <cr><lf>を付けて速度をGPSに指示(2回目)
delay(50);
Serial1.end(); // シリアルを一度止める
delay(50);
Serial1.begin(38400); // Serial1(PA9 PA10) オープン
break;
case 0:
default:
Serial1.println(bps96set); // <cr><lf>を付けて速度をGPSに指示
delay(50);
Serial1.println(bps96set); // <cr><lf>を付けて速度をGPSに指示(2回目)
delay(50);
Serial1.end(); // シリアルを一度止める
delay(50);
Serial1.begin(9600); // Serial1(PA9 PA10) オープン
break;
}
if (bpsPRM != tmpprm) { // 書き替えが有った
write(0, bpsPRM); // ここにROM書込み
}
break;
case 1: // SEND to HOST
tmpprm = hostPRM;
hostPRM = confPRMset(j, tmpprm); // 新しい方向をセット
if (hostPRM != tmpprm) { // 書き替えが有った
write(1, hostPRM); // ここにROM書込み
}
break;
case 2: // GATE time
tmpprm = gatePRM;
gatePRM = confPRMset(j, tmpprm);
if (gatePRM != tmpprm) { // 書き替えが有った
write(2, gatePRM); // ここにROM書込み
}
break;
} // switch (j) end
break;
} // switch (buttonSW()) end
if (stateRet == 1) { // jobSelect(uint8_t)を抜ける
return j;
}
} // while (1) end
}
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// ◆◆◆◆◆M ボタンの状態を返す◆◆
// 何れかのボタンが押されて放された(F0=10,F1=11,F2=12,F3=13)を待つ。
// 旧設定
// F0 < 80, 300 < F1 > 430, 580 < F2 < 790, 800 < F3 < 1100
// New (2020/04/16)
// F0 < 109, 110 < F1 > 548, 549 < F2 < 823, 823 < F3 < 1200,
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
int8_t buttonSW() {
int8_t retVal = 0; // = 0 の記述は必須。
int8_t repeat10 = 0;
int8_t count = 0;
uint16_t asw;
// while (1) {
asw = analogRead(SWa_In);
if (asw < 111) { // F0ボタンなら return 10
while (asw < 111) { // F0ボタンを放すまで待つ
delay(50);
asw = analogRead(SWa_In);
count++;
if (count >= 20) { // 1000mS 以上押されたら10プラス
repeat10 = 10;
}
}
delay(50);
return 10 + repeat10;
}
if ((asw > 110) && (asw < 549)) { // F1ボタンなら Plus
while ((asw > 110) && (asw < 549)) { // F1ボタンを放すまで待つ
delay(50);
asw = analogRead(SWa_In);
count++;
if (count >= 20) { // 1000mS 以上押されたら10プラス
repeat10 = 10;
}
}
delay(50);
return 11 + repeat10;
}
if ((asw > 548) && (asw < 824)) { // F2ボタンなら Minus
while ((asw > 548) && (asw < 824)) { // F2ボタンを放すまで待つ
delay(50);
asw = analogRead(SWa_In);
count++;
if (count >= 20) { // 1000mS 以上押されたら10プラス
repeat10 = 10;
}
}
delay(50);
return 12 + repeat10;
}
if ((asw > 823) && (asw < 1200)) { // F3ボタンなら return 13
while ((asw > 823) && (asw < 1200)) { // F3ボタンを放すまで待つ
delay(50);
asw = analogRead(SWa_In);
count++;
if (count >= 20) { // 1000mS 以上押されたら10プラス
repeat10 = 10;
}
}
delay(50);
return 13 + repeat10;
}
// } // while end
}
///////////////////////////////////////////////////////////////////////
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// 処理選択画面を展開し、選択された値を返す。
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
uint8_t confPRMset(uint8_t jobNo, uint8_t prm)
{
int8_t i, j, p, xl;
int8_t stateRet = 0;
const char* titlMsg[] = {
"0.UART BPS Set",
"1.SEND to HOST",
"2.First GATE time"
};
const char selMsg[3][3][22] = {
{ "9,600 bps(default)", // jobNo = 0
"19,200 bps(32.6%)",
"38,400 bps(16.3%)"
},
{ "GPS data(default)", // jobNo = 1
"LOG data",
"PIC"
},
{ "1 sec(default)", // jobNo = 2
"10 sec",
"100 sec"
}
};
int8_t item;
uint8_t tmpprm = 0;
switch (jobNo) { // 選択する項目(selMsg)の数を処理No毎にセットする。012
case 0:
item = 2;
break;
case 1:
item = 2;
break;
case 2:
item = 2;
break;
};
j = prm;
p = 0;
while (1)
{
u8g2.clearBuffer(); // OLEDクリヤー
u8g2.setDrawColor(1); // 反転用の処理のため
u8g2.drawBox(0, 2, 128, 12); // Box描画
u8g2.setDrawColor(2); // color 2 set
Text_setCursor(0, 0); // 桁・行
u8g2.print(titlMsg[jobNo]); // タイトルの行を表示
for (i = 0; i <= item; i++)
{
Text_setCursor(2, i + 1); // 桁・行
u8g2.print(selMsg[jobNo][i]);
}
xl = (j + 2) * 12; // マーカー位置指定
u8g2.drawGlyph(5, xl , 0x25b6); // 0x21d2⇒ 0x25b6?
func_print(0, "RETN");
func_print(1, " UP ");
func_print(2, "Down");
func_print(3, "ENTR");
u8g2.sendBuffer(); // OLEDに送信(描画する)
switch (buttonSW()) {
case 10:
tmpprm = prm; // 初期値で戻る
stateRet = 1;
break;
case 11:
case 21:
j--;
if (j < 0) j = item;
break;
case 12:
case 22:
j++;
if (j > item) j = 0;
break;
case 13:
switch (j) {
case 0:
case 1:
case 2:
tmpprm = j;
stateRet = 1; // 終了処理をセット
break;
} // switch (j) end
break;
} // switch (buttonSW()) end
if (stateRet == 1) { // jobSelect(int8_t)を抜ける
return tmpprm;
}
} // while (1) end
}
///////////////////////////////////////////////////////////////////////
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// EEPROM 24C256 等の外部EEPROMのI2C読み書き関数。
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
// 1バイト書込
uint8_t write(uint16_t address, uint8_t data) {
uint8_t rc;
Wire.beginTransmission(DEVICE_ADDRESS);
Wire.write((byte)(address >> 8)); // アドレス上位
Wire.write((byte)(address & 0xFF)); // アドレス下位
Wire.write(data);
rc = Wire.endTransmission();
delay(6);
return rc;
}
// 1バイト読込
uint8_t read(uint16_t address) {
Wire.beginTransmission(DEVICE_ADDRESS);
Wire.write((byte)(address >> 8)); // アドレス上位
Wire.write((byte)(address & 0xFF)); // アドレス下位
Wire.endTransmission();
Wire.requestFrom(DEVICE_ADDRESS, 1);
return Wire.read();
}
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■