/************************************************* * EMF Meter Program * * Written for issue Jan. 2015 of Monthly FB News * * T.Tsujioka (JH1NRR/JL3YMC) * *************************************************/ // Last updated: 2015/01/18 #include #include // #include #define _XTAL_FREQ 40000000 // Internal clock of 40MHz #pragma config PMDL1WAY = OFF // Peripheral Module DIsable Configuration #pragma config IOL1WAY = OFF // Peripheral Pin Select Configuration #pragma config FUSBIDIO = ON // USB USID Selection #pragma config FVBUSONIO = ON // USB VBUS ON Selection #pragma config FPLLIDIV = DIV_2 // PLL Input Divider (4MHz) #pragma config FPLLMUL = MUL_20 // PLL Multiplier (80MHz) #pragma config UPLLIDIV = DIV_5 // USB PLL Input Divider #pragma config UPLLEN = OFF // USB PLL Enable #pragma config FPLLODIV = DIV_2 // System PLL Output Clock Divider (40MHz) #pragma config FNOSC = FRCPLL // Oscillator Selection Bits (内蔵8MHz+PLL) #pragma config FSOSCEN = OFF // Secondary Oscillator Enable #pragma config IESO = OFF // Internal/External Switch Over #pragma config POSCMOD = OFF // Primary Oscillator Configration (外付けPrimary XTALなし) #pragma config OSCIOFNC = OFF // CLKO Output Signal Active on the OSCO Pin #pragma config FPBDIV = DIV_2 // Peripheral Clock Divisor #pragma config FCKSM = CSDCMD // Clock Switching and Monitor Selection #pragma config WDTPS = PS1 // Watchdog Timer Postscaler #pragma config WINDIS = OFF // Watchdog Timer Window Enable #pragma config FWDTEN = OFF // Watchdog Timer Enable #pragma config FWDTWINSZ = WISZ_25 // Watchdog Timer Window Size #pragma config JTAGEN = OFF // JTAG Enable #pragma config ICESEL = ICS_PGx1 // ICE/ICD Comm Channel Select #pragma config PWP = OFF // Program Flash Write Protect #pragma config BWP = OFF // Boot Flash Write Protect bit #pragma config CP = OFF // Code Protect // ----- subroutines ----- #define GetSystemClock() (_XTAL_FREQ) #define GetPeripheralClock() (GetSystemClock() / (1 << OSCCONbits.PBDIV)) void delay_10us(int us) { int i; while (us-- > 0) { for (i = 0; i < 21; i++) { // for 40MHz system clock asm("nop"); asm("nop"); asm("nop"); asm("nop"); } } } void delay_ms(int ms) { while (ms-- > 0) { delay_10us(100); } } // ----- LCD関係 ----- void lcd_write(char addr, char dat1, char dat2) { #if 1 while (!I2CBusIsIdle(I2C1)); I2CStart(I2C1); while (!(I2CGetStatus(I2C1) & I2C_START)); while (!I2CTransmitterIsReady(I2C1)); I2CSendByte(I2C1, addr); while (!I2CTransmissionHasCompleted(I2C1)); if (!I2CByteWasAcknowledged(I2C1)) return; // NACK'ed by slave while (!I2CTransmitterIsReady(I2C1)); I2CSendByte(I2C1, dat1); while (!I2CTransmissionHasCompleted(I2C1)); if (!I2CByteWasAcknowledged(I2C1)) return; // NACK'ed by slave while (!I2CTransmitterIsReady(I2C1)); I2CSendByte(I2C1, dat2); while (!I2CTransmissionHasCompleted(I2C1)); if (!I2CByteWasAcknowledged(I2C1)) return; // NACK'ed by slave I2CStop(I2C1); #else StartI2C1(); IdleI2C1(); MasterWriteI2C1(addr); IdleI2C1(); if (I2C1STATbits.ACKSTAT) return; // NACK'ed by slave MasterWriteI2C1(dat1); IdleI2C1(); if (I2C1STATbits.ACKSTAT) return; // NACK'ed by slave MasterWriteI2C1(dat2); IdleI2C1(); if (I2C1STATbits.ACKSTAT) return; // NACK'ed by slave // data = MasterReadI12C1(); // AckI2C1(); // IdleI2C1(); StopI2C1(); IdleI2C1(); #endif } void lcd_write_command(char cmd) { lcd_write(0xa0, 0x00, cmd); } void lcd_write_data(char dat) { lcd_write(0xa0, 0x80, dat); } void lcd_init(void) { delay_ms(200); lcd_write_command(0x01); // Clear Display delay_ms(15); lcd_write_command(0x38); // Function Set: DL=1, N=1, F=1 delay_ms(15); lcd_write_command(0x0c); // Display Control: D=1, C=0, B=0 delay_ms(15); lcd_write_command(0x06); // Entry Mode: I/D=1, S=0 delay_ms(15); } void lcd_print(char line, char *str) { char cmd_loc; int i; if (line == 0) cmd_loc = 0x80; // DDRAM:AC=0x00 (Line-1) else cmd_loc = 0xc0; // DDRAM:AC=0x40 (Line-2) lcd_write_command(cmd_loc); delay_ms(5); for (i = 0; i < 16; i++) { if (*str != 0) { lcd_write_data(*str++); delay_ms(5); } else { lcd_write_data(' '); delay_ms(5); } } } // ----- ADC関係 ----- #define ADC_SIGNAL_SIZE 1024 short adc_signal[ADC_SIGNAL_SIZE]; short adc_signal_peak; short adc_signal_min; short adc_signal_mean; double signal_mean, signal_variance; int count_signal; int total_adc_signal_peak; double total_signal_mean, total_signal_variance; int total_count_signal; int total_measurements; // 積算統計量のリセット void emf_clear(void) { total_adc_signal_peak = 0; total_count_signal = 0; total_signal_mean = 0.0; total_measurements = 0; } // 信号取り込みと統計量計算 void emf_measure(char ch) { int i; double sum, sum2; char status; short threshold; // --- 信号強度(対数値)の取り込み --- if (ch == 0) { SetChanADC10(ADC_CH0_POS_SAMPLEA_AN0 | ADC_CH0_NEG_SAMPLEA_NVREF); } else { SetChanADC10(ADC_CH0_POS_SAMPLEA_AN1 | ADC_CH0_NEG_SAMPLEA_NVREF); } for (i = 0; i < ADC_SIGNAL_SIZE; i++) { ConvertADC10(); while (BusyADC10()); adc_signal[i] = ReadADC10(0); } // --- 統計量の計算 --- // calc the mean and the variance from moments sum = 0; sum2 = 0; for (i = 0; i < ADC_SIGNAL_SIZE; i++) { sum += (double)adc_signal[i]; sum2 += (double)adc_signal[i] * adc_signal[i]; } signal_mean = sum / (double)ADC_SIGNAL_SIZE; adc_signal_mean = (short)signal_mean; signal_variance = (sum2 - ADC_SIGNAL_SIZE * signal_mean * signal_mean) / (ADC_SIGNAL_SIZE - 1); // 不偏分散 // find peak & min adc_signal_peak = 0; adc_signal_min = 0; for (i = 0; i < ADC_SIGNAL_SIZE; i++) { if (adc_signal[i] > adc_signal_peak) { adc_signal_peak = adc_signal[i]; } if (adc_signal[i] < adc_signal_peak || !adc_signal_min) { adc_signal_min = adc_signal[i]; } } #if 1 threshold = (adc_signal_peak + adc_signal_mean) / 2; #elif 0 threshold = (int)(signal_mean + sqrt(signal_variance)); #else threshold = adc_signal_mean; #endif // no. of signals count_signal = 0; status = 0; for (i = 0; i < ADC_SIGNAL_SIZE; i++) { if (status == 0 && adc_signal[i] > threshold) { count_signal++; status = 1; } else if (status == 1 && adc_signal[i] < adc_signal_mean) { status = 0; } } #if 1 if (adc_signal_peak - adc_signal_min < 20) { count_signal = 0; // ignore noise } #else if (signal_variance < 400) { count_signal = 0; // ignore noise } #endif // continuous measurements if (total_adc_signal_peak < adc_signal_peak) { total_adc_signal_peak = adc_signal_peak; } total_count_signal += count_signal; total_signal_mean = (total_signal_mean * total_measurements + signal_mean) / (total_measurements + 1.0); total_signal_variance = (total_signal_variance * total_measurements + signal_variance) / (total_measurements + 1.0); total_measurements++; } #define SW1 (PORTAbits.RA3) #define SW2 (PORTAbits.RA2) #define SW3 (PORTBbits.RB3) #define SW4 (PORTBbits.RB2) // 電界強度測定と表示 // ※実際には空間の電界強度[V/m]というよりは受信信号強度(RSSI)[dBm]を測定 void emf_meter(void) { char ch; char buf[22]; double mean, v_mean, p_mean; double peak, v_peak, p_peak; double slope, intercept; int cnt; char total_mode = 0; char prev_SW4 = 1; emf_clear(); ch = 0; while (1) { if (SW1 == 0) { // ADC8317(AN0入力)に切り替える ch = 0; emf_clear(); } if (SW2 == 0) { // ADC8313(AN1入力)に切り替える ch = 1; emf_clear(); } if (SW3 == 0) { // 積算統計量をリセット emf_clear(); } if (SW4 == 0 && prev_SW4 != 0) { // カレント測定値と積算値を切り替える total_mode = !total_mode; } prev_SW4 = SW4; emf_measure(ch); // 表示項目 if (!total_mode) { mean = signal_mean; peak = adc_signal_peak; cnt = count_signal; } else { mean = total_signal_mean; peak = total_adc_signal_peak; cnt = total_count_signal; } // 表示項目のdBm換算 if (ch == 0) { // AD8317 (DC to 500MHz) slope = 0.019; // 対数の傾き intercept = -89.0; // 対数切片 } else { // AD8313 (100MHz to 2.5GHz) slope = 0.025; // 対数の傾き intercept = -84.0; // 対数切片 } v_mean = mean * (3.3 / 1024); v_peak = peak * (3.3 / 1024); p_mean = v_mean / slope + intercept; p_peak = v_peak / slope + intercept; sprintf(buf, "CH%d %5.1fdBm %c", ch, p_mean, (total_mode? 'T': ' ')); lcd_print(0, buf); sprintf(buf, "pk:%5.1f tx:%d", p_peak, cnt); lcd_print(1, buf); // delay_ms(100); // 測定値の表示更新間隔が短く、液晶ディスプレイの反応が追随していない場合は、ここにディレイを入れて下さい。 } } // メイン関数 void main(void) { #if 0 SYSTEMConfigPerformance(_XTAL_FREQ); mJTAGPortEnable(DEBUG_JTAGPORT_OFF); // JTAGを無効化 #endif TRISA = 0x0f; // input:RA3(SW1),RA2(SW2),AN1,AN0 TRISB = 0x0c; // input:RB3(SW3),RB2(SW4) CNPUA = 0x0c; // weak pull-up:RA3-2 CNPUB = 0x0c; // weak pull-up:RB3-2 ANSELB = 0x03; // Disable analog input except AN1 and AN0 // Remappableピンの割付(PPS: Peripheral Pin Select) PPSOutput(1, RPB15, U1TX); PPSOutput(3, RPA4, SDO1); PPSInput(2, SDI1, RPB5); PPSInput(3, U1RX, RPB13); // 初期化:I2C I2CConfigure(I2C1, I2C_ENABLE_SLAVE_CLOCK_STRETCHING /* | I2C_ENABLE_HIGH_SPEED */); I2CSetFrequency(I2C1, GetPeripheralClock(), 50000); // 50kHz // I2CSetSlaveAddress(I2C1, my_slave_address, 0, I2C_USE_7BIT_ADDRESS) I2CEnable(I2C1, TRUE); // 初期化:LCD lcd_init(); // 初期化:ADC // ADC_SAMPLES_PER_INT_2 -> 使うbufferの深さと関係 OpenADC10(ADC_MODULE_ON | ADC_IDLE_CONTINUE | ADC_FORMAT_INTG16 | ADC_CLK_MANUAL | ADC_AUTO_SAMPLING_ON | ADC_SAMP_ON, ADC_VREF_AVDD_AVSS | ADC_OFFSET_CAL_DISABLE | ADC_SCAN_OFF | ADC_SAMPLES_PER_INT_2 | ADC_ALT_BUF_OFF | ADC_ALT_INPUT_ON, ADC_CONV_CLK_PB | ADC_SAMPLE_TIME_2 | ADC_CONV_CLK_3Tcy, ENABLE_AN0_ANA | ENABLE_AN1_ANA, SKIP_SCAN_ALL); ConfigIntADC10(ADC_INT_OFF | ADC_INT_PRI_2 | ADC_INT_SUB_PRI_3); EnableADC10(); SetChanADC10(ADC_CH0_POS_SAMPLEA_AN0 | ADC_CH0_NEG_SAMPLEA_NVREF); // 電界強度測定 emf_meter(); }