// // FM/AM TUNER // ver 1.0 2024-Nov // ver 1.1 2024-Nov ADD MUTE @FM // ver 1.2 2024-Dec Delete MUTE @FM // ADC routine,SW sample rate long time // (c)OKIRAKU AUDIO // // #include "18F27Q43.H" #device ADC=12 #fuses RSTOSC_HFINTRC_64MHZ,PROTECT,NOMCLR,NOLVP #use delay(internal=64MHz) #use fast_io(A) #use fast_io(B) #use fast_io(C) #define SEL_SW PIN_B5 // SW1 #define MUTE_SW PIN_B4 // SW2 #define POWER_SW PIN_B4 // SW2 #define RENC_1 PIN_B3 // ROTARY ENCORDER INPUT1 #define RENC_2 PIN_B2 // ROTARY ENCORDER INPUT2 #define I2C_SDA PIN_C1 // I2C for KT09XX #define I2C_SCL PIN_C0 // I2C for KT09XX #define MUTE_LED PIN_A5 #define POWER_LED PIN_A5 // // JP2 JP1 // 1 1 SC2004 and small_led4 SC2004 // 1 0 SC1602 and small_led4 SC1602 // 0 1 SH1106 and small_led4 SH1106 // 0 0 SSD1306 and small_led4 SSD1306 #define JP2 PIN_B0 // #define JP1 PIN_B1 // #define SC2004 (0) // 20 x 4 LCD #define SC1602 (1) // 16 x 2 LCD #define SH1106 (2) // 128 x 64 GLCD #define SSD1306 (3) // 128 x 64 GLCD int display_type = SC2004; #define SERIAL_OUT PIN_A1 // small_LED4 unsigned int16 serial_data[4]; // serial LED4 data buffer bit10:COLOR 9,8:ROW 7-0:segment unsigned int serial_data_num=0; // 0:first 3:last 4>=n not action #include "small_led4_func.c" #define LCD_SC_RS PIN_C4 // SC2004 #define LCD_SC_E PIN_C5 #define LCD_SC_D4 PIN_C6 #define LCD_SC_D5 PIN_C7 #define LCD_SC_D6 PIN_C2 #define LCD_SC_D7 PIN_C3 #include "lcdlib.c" #define AUX_A2 PIN_A2 #define AUX_A3 PIN_A3 #define AUX_A4 PIN_A4 int lcd_type=1; // 1:SC2004 #include "glcdlib_ssd1306_sh1106.c" #define I2C_KT (0x6a) #include "i2c_multi.c" int16 fm_freq; // FM FREQ 80.2MHz is 802 int16 am_freq; // AM FREQ 1034kHz is 1034 int16 volume; // 0:(MUTE),1:-60dB 2:-58dB,,,,31:0dB int rec_mode; // receiver mode #define FM_MODE (0) #define AM_MODE (1) int snr_level=50; // MUTE level of SNR int mute_func=0; // 0:MUTE OFF 1:MUTE ACTIVE @FM int16 current_snr; #define NORMAL_MODE (0) #define SNRSET_MODE (1) int current_mode=NORMAL_MODE; signed int16 rstep1=0; int16 return_loop = 0; int16 start_count = 0; int16 rdur1=0; int sw1_status=0; int sw1_long_push=0; int sw1_long_long=0; int16 rdur2=0; int sw2_status=0; int sw2_long_push=0; int sw2_long_long=0; #define WAITLOOP (300) #define LONG_PUSH_TIME (500) #define LONG_PUSH_REPEAT (300) #define LONG_LONG_PUSH_TIME (3000) #define LONG_LONG_PUSH_REPEAT (700) int16 loopnum = 0; int16 loopnum2 = 0; #define ID_TOP (0) #define FM_FREQ_TOP (10) #define AM_FREQ_TOP (12) #define VOLUME_TOP (14) #define REC_MODE_TOP (16) #define SNR_LEVEL_TOP (18) #define MUTE_FUNC_TOP (20) int16 get_ad() // single get { int1 done; int16 value; read_adc(ADC_START_ONLY); done = adc_done(); while(!done) done=adc_done(); value=read_adc(ADC_READ_ONLY); return(value); } #define UP_DIR (1) #define DOWN_DIR (0) #define ADC_THR (4) int16 get_ad16() { static int loop=0; static unsigned int16 ad=0; static unsigned int16 av=0; static int dir = UP_DIR; static int16 old_ad=0; int16 adc; adc = get_ad(); if(dir == UP_DIR){ if(adc > old_ad){ old_ad = adc; } else { if( (adc+ADC_THR) < old_ad ){ old_ad = adc; dir = DOWN_DIR; } } } else { if(adc < old_ad){ old_ad = adc; } else { if( adc > (old_ad+4) ){ old_ad = adc; dir = UP_DIR; } } } ad += old_ad; loop++; if(loop == 16){ av = ad >> 4; ad = 0; loop=0; } return(av); } void common_position(int x,int y) { switch(display_type){ case SC2004: case SC1602: lcd_position(x,y); break; case SH1106: case SSD1306: glcd_position(x,y); break; default: break; } } void common_char(char c) { switch(display_type){ case SC2004: case SC1602: lcd_char(c); break; case SH1106: case SSD1306: glcd_char(c); break; default: break; } } void common_clear() { int i; switch(display_type){ case SC2004: for(i=0;i<4;i++){ lcd_position(0,i);printf(lcd_char," "); } common_position(0,0); break; case SC1602: for(i=0;i<4;i++){ lcd_position(0,i);printf(lcd_char," "); } common_position(0,0); break; case SH1106: case SSD1306: for(i=0;i<8;i++){ glcd_position(0,i);printf(glcd_char," "); } common_position(0,0); break; default: break; } } void write_eeprom16(int16 n,int16 data) { write_eeprom(n, data & 0xff); write_eeprom(n+1,(data >> 8) & 0xff); } int16 read_eeprom16(int16 n) { int16 a,b; a=read_eeprom(n);a &= 0xff; b=read_eeprom(n+1); b = b << 8; b &= 0xff00; b |= a; return(b); } int check_initialize() { int j; j=0; if(read_eeprom(0)!='O') j++; if(read_eeprom(1)!='K') j++; if(read_eeprom(2)!='I') j++; if(read_eeprom(3)!='R') j++; if(read_eeprom(4)!='A') j++; if(read_eeprom(5)!='K') j++; if(read_eeprom(6)!='U') j++; if(input(SEL_SW)==0) j++; if(input(MUTE_SW)==0) j++; return(j); } void initialize_parameter() { write_eeprom(0,'O'); write_eeprom(1,'K'); write_eeprom(2,'I'); write_eeprom(3,'R'); write_eeprom(4,'A'); write_eeprom(5,'K'); write_eeprom(6,'U'); write_eeprom16(FM_FREQ_TOP,fm_freq=765); write_eeprom16(AM_FREQ_TOP,am_freq=666); write_eeprom(VOLUME_TOP,volume = 0); write_eeprom(REC_MODE_TOP,rec_mode = FM_MODE); write_eeprom(SNR_LEVEL_TOP,snr_level=40); write_eeprom(MUTE_FUNC_TOP,mute_func=0); } void load_parameter() { fm_freq=read_eeprom16(FM_FREQ_TOP); if(fm_freq > 960 || fm_freq <750) fm_freq = 765; am_freq=read_eeprom16(AM_FREQ_TOP); if(am_freq > 1620 || am_freq < 531) am_freq = 666; volume = read_eeprom(VOLUME_TOP); if(volume >31 ) volume = 0; rec_mode = read_eeprom(REC_MODE_TOP); if(rec_mode != FM_MODE && rec_mode != AM_MODE) rec_mode = FM_MODE; snr_level = read_eeprom(SNR_LEVEL_TOP); if(snr_level > 99) snr_level = 99; mute_func = read_eeprom(MUTE_FUNC_TOP); if(mute_func & 0xfe) mute_func = 0; } void store_parameter() { if(fm_freq!=read_eeprom16(FM_FREQ_TOP)) write_eeprom16(FM_FREQ_TOP,fm_freq); if(am_freq!=read_eeprom16(AM_FREQ_TOP)) write_eeprom16(AM_FREQ_TOP,am_freq); if(volume !=read_eeprom(VOLUME_TOP)) write_eeprom(VOLUME_TOP,volume); if(rec_mode!=read_eeprom(REC_MODE_TOP)) write_eeprom(REC_MODE_TOP,rec_mode); if(snr_level!=read_eeprom(SNR_LEVEL_TOP)) write_eeprom(SNR_LEVEL_TOP,snr_level); if(mute_func!=read_eeprom(MUTE_FUNC_TOP)) write_eeprom(MUTE_FUNC_TOP,mute_func); } // // KR9013 function set // void set_rec_mode() { int16 a; a = i2c_datain16(I2C_KT,0x16); if(rec_mode==AM_MODE) a |= 0x8000; if(rec_mode==FM_MODE) a &= 0x7fff; i2c_dataout16(I2C_KT,0x16,a); } void set_fm_freq() { i2c_dataout16(I2C_KT,0x03,fm_freq * 2 | 0x8000); // start tune i2c_dataout16(I2C_KT,0x03,fm_freq * 2); // normal operation } void set_am_freq() { i2c_dataout16(I2C_KT,0x17,am_freq); } void set_volume(int n) { int16 a; a=n; a&=0x1f; i2c_dataout16(I2C_KT,0x0f,0x8800 | a); } // // display set // void disp_rec_mode() { common_position(0,0); if(rec_mode==AM_MODE) printf(common_char,"AM"); else printf(common_char,"FM"); } void disp_rssi() { int16 a; static int16 am=0,fm=0; static int16 loopam=0,loopfm=0; if(rec_mode==FM_MODE){ a=i2c_datain16(I2C_KT,0x12); a = a >> 3; a &= 0x1f; fm += a; loopfm++; if(loopfm == 128){ fm = fm >> 7; fm *= 3; fm = -100+fm; loopfm=0; switch(display_type){ case SC2004: case SH1106: case SSD1306: common_position(0,2); printf(common_char,"FM RSSI %4lddBm ",fm); break; case SC1602: common_position(0,1); if(fm == -100) fm=-99; printf(common_char,"%3lddBm",fm); break; default:break; } fm=0; loopfm=0; } } else { a=i2c_datain16(I2C_KT,0x24); a = a >> 8; a &= 0x1f; am += a; loopam++; if(loopam==128){ am = am >> 7; am *= 3; am = -90+am; switch(display_type){ case SC2004: case SH1106: case SSD1306: common_position(0,2); printf(common_char,"AM RSSI %4lddBm ",am); break; case SC1602: common_position(0,1); if(am == -100) am= -99; printf(common_char,"RSSI %3lddBm",am); break; default:break; } am=0; loopam=0; } } } void disp_snr() { int16 a; static int16 v=0; static int16 loop=0; if(rec_mode==AM_MODE) return; a=i2c_datain16(I2C_KT,0x14); a = a >> 6; a &= 0x7f; if(v > a) v=a; loop++; if((loop%32)==0){ current_snr = v; v = 127; } if((loop%256)==0){ switch(display_type){ case SC2004: case SH1106: case SSD1306: common_position(0,3); printf(common_char,"CH SNR %3ld(0-127)",current_snr); /* if(current_mode==NORMAL_MODE){ if(mute_func){ printf(common_char,":THR"); if(snr_level<10) printf(common_char,"0%1d",snr_level); else printf(common_char,"%2d",snr_level); } else printf(common_char," "); } else{ printf(common_char,":???"); if(snr_level<10) printf(common_char,"0%1d",snr_level); else printf(common_char,"%2d",snr_level); } */ break; case SC1602: common_position(8,1); printf(common_char," SNR%3ld",current_snr); if(current_mode==NORMAL_MODE){ if(mute_func){ printf(common_char," SNR"); if(snr_level<10) printf(common_char,"0%1d",snr_level); else printf(common_char,"%2d",snr_level); } else printf(common_char," "); } else { printf(common_char,":?"); if(snr_level<10) printf(common_char,"0%1d",snr_level); else printf(common_char,"%2d",snr_level); } break; default: break; } } } void disp_volume() // printf "-xxdB" 5characters { char buf[10]; int16 a; if(volume==0) sprintf(buf," MUTE"); else { a = volume; a = 31-a; a = a << 1; if(a==0) sprintf(buf," 0dB"); else if(a<=9) sprintf(buf," -%1lddB",a); else sprintf(buf,"-%2lddB",a); } switch(display_type){ case SC2004: case SH1106: case SSD1306: common_position(0,1); printf(common_char,"Volume "); printf(common_char,buf); break; case SC1602: common_position(11,0); printf(common_char,buf); break; default: break; } } void disp_reg(int n) { int16 a; a=i2c_datain16(I2C_KT,n); printf(lcd_char,"R%2x %4lx",n,a); } void disp_fm_freq() // "xx.xMHz" 7 characters { int16 a; a=fm_freq; common_position(3,0); printf(common_char,"%2ld.",a/10); printf(common_char,"%1ldMHz",a%10); } void disp_fm_freq_sled4() { int16 a; a=fm_freq; small_led4_integer_out(a,0); serial_data[2] |= 0x80; } void disp_am_freq() // "xxxxkHz" 7 characters { int16 a; a=am_freq; common_position(3,0); printf(common_char,"%4ldkHz",a); } void disp_am_freq_sled4() { int16 a; a=am_freq; small_led4_integer_out(a,1); } void disp_snr_level_sled4() { int16 a; a=snr_level; small_led4_integer_out(a,0); } #INT_TIMER0 rtcc_isr() // interrupt every 1.024ms { static int enc_current[2]; static int enc_old[2]; static int prepare=1; int enc_change; static int sw_loop=0; int i,j,k,l; int r1a_old=0; int r1b_old=0; int r1b_change=0; static int16 adc_average = 0; int16 adc_result_local,old_adc; static int sw1_old = 1; static int16 sw1_dur = 0; static int16 sw1_dur2 = 0; static int sw2_old = 1; static int16 sw2_dur = 0; static int16 sw2_dur2 = 0; int code,num; static int old_ir; int1 done; static int blink_counter=0; static int blink_status=0; // 0:ON 1:OFF int16 result; int16 v; int adc_loop; static int16 store_loop=0; set_timer0(7); output_high(AUX_A4); if(prepare){ enc_current[0]=enc_old[0]=input(RENC_1); enc_current[1]=enc_old[1]=input(RENC_2); prepare=0; } // small_led4 start if(serial_data_num <4) output_low(SERIAL_OUT); // start bit // if((sw_loop&3) == 0){ // every 4ms // // ENCORDER SENCE // enc_current[0]=input(RENC_1); enc_current[1]=input(RENC_2); if(enc_current[1]!=enc_old[1]){enc_change = 1; enc_old[1] = enc_current[1];} if(enc_current[0]==0){ if(enc_old[0]){ if(enc_change){ if(enc_current[1]) rstep1 = -1; else rstep1=1; } enc_old[0]=0; } } else enc_old[0] = 1; if(current_mode == NORMAL_MODE){ if(rec_mode == FM_MODE){ if(rstep1>0){ while(rstep1){ rstep1--; if(fm_freq!=960) fm_freq++; } } else if(rstep1<0){ while(rstep1){ rstep1++; if(fm_freq !=750) fm_freq--; } } } else { if(rstep1>0){ while(rstep1){ rstep1--; if(am_freq < 1620) am_freq+=9; } } else if(rstep1<0){ while(rstep1){ rstep1++; if(am_freq > 531) am_freq-=9; } } } } else { if(rstep1>0){ while(rstep1){ rstep1--; if(snr_level < 99) snr_level++; } } else if(rstep1<0){ while(rstep1){ rstep1++; if(snr_level > 0) snr_level--; } } } // // PUSH-SW SENCE // i=input(SEL_SW); if(i==0){ if(sw1_old != 0) sw1_status = 1; } sw1_old = i; /* i=input(MUTE_SW); if(i==0){ if(sw2_old != 0) sw2_status = 1; if(sw2_dur != LONG_PUSH_TIME) sw2_dur++; else{ sw2_long_push = 1; } } else { sw2_dur=0; } sw2_old = i; */ sw2_status = 0; // sw_loop++; // } volume = get_ad16() >> 7; // parameter store store_loop++; if(store_loop==3000){ store_parameter(); store_loop=0; } // small_led4 // another process >30us if(serial_data_num<4){ serial_out_routine(serial_data[serial_data_num]); serial_data_num++; } // if(start_count != 10000) start_count++; // end of interval output_low(AUX_A4); } void io_select() { set_tris_a(0xff); set_tris_b(0xff); set_tris_c(0xff); port_b_pullups(0xff); setup_adc_ports(sAN0); set_adc_channel(0); setup_adc(ADC_CLOCK_DIV_64 | ADC_TAD_MUL_16); // output_drive(I2C_GLCD_SCL);output_high(I2C_GLCD_SCL); // output_float(I2C_GLCD_SDA);output_high(I2C_GLCD_SDA); output_drive(I2C_SCL);output_high(I2C_SCL); output_float(I2C_SDA);output_high(I2C_SDA); output_drive(SERIAL_OUT);output_high(SERIAL_OUT); output_drive(LCD_SC_RS); output_drive(LCD_SC_E);output_low(LCD_SC_E); output_drive(LCD_SC_D4); output_drive(LCD_SC_D5); output_drive(LCD_SC_D6); output_drive(LCD_SC_D7); output_drive(MUTE_LED);output_low(MUTE_LED); output_drive(AUX_A4); } restart_config() { set_rec_mode(); if(rec_mode==FM_MODE) set_fm_freq(); if(rec_mode==AM_MODE) set_am_freq(); i2c_dataout16(I2C_KT,0x0f,0x8800); // RFCFG i2c_dataout16(I2C_KT,0x04,0xe080); // VOLUME if(rec_mode==FM_MODE) set_fm_freq(); if(rec_mode==AM_MODE) set_am_freq(); set_rec_mode(); } void main() { // int oled_display = SH1106; int16 id_code; int16 i=0; int s1,s2,j; int16 a; int16 old_fm_freq,old_am_freq,old_volume; setup_oscillator(OSC_HFINTRC_8MHZ); // slow starf delay_ms(1); setup_oscillator(OSC_HFINTRC_16MHZ); // slow starf delay_ms(20); setup_oscillator(OSC_HFINTRC_64MHZ); delay_ms(100); io_select(); delay_ms(200); // select display type s2 = input(JP2); s1=input(JP1); if(s2 && s1 ) display_type = SC2004; if(s2 && s1==0) display_type = SC1602; if(s2==0 && s1 ){display_type = SH1106; glcd_type = GLCD_SH1106; } if(s2==0 && s1==0){display_type = SSD1306;glcd_type = GLCD_SSD1306; } // display initialize switch(display_type){ // LCD Initialize case SC2004: case SC1602: lcd_init(); lcd_home(); break; case SH1106: glcd_init(1); glcd_clear(); glcd_position(0,0); break; case SSD1306: glcd_init(1); glcd_clear(); glcd_position(0,0); break; default:break; } // opening message switch(display_type){ case SC1602: common_position(0,0);printf(common_char,"FM/AM TUNER "); common_position(0,1);printf(common_char,"with KT0913 v1.2"); break; default: common_position(0,0); printf(common_char,"* FM/AM TUNER *"); common_position(0,1); printf(common_char,"* with KT0913 v1.2 *"); common_position(0,2); printf(common_char,"* OKIRAKU AUDIO *"); common_position(0,3); printf(common_char,"* 2023-Nov *"); break; } delay_ms(1500); common_clear(); volume = 0; set_volume(0); i2c_dataout16(I2C_KT,0x0f,0x8800); // RFCFG Enable vol=MUTE i2c_dataout16(I2C_KT,0x04,0xe080); // VOLUME MUTE Diasalbe if(check_initialize()) initialize_parameter(); load_parameter(); set_rec_mode(); if(rec_mode==FM_MODE) set_fm_freq(); if(rec_mode==AM_MODE) set_am_freq(); i2c_dataout16(I2C_KT,0x0f,0x8800); // RFCFG i2c_dataout16(I2C_KT,0x04,0xe080); // VOLUME if(rec_mode==FM_MODE) set_fm_freq(); if(rec_mode==AM_MODE) set_am_freq(); set_rec_mode(); old_fm_freq = fm_freq; old_am_freq = am_freq; old_volume = volume; if(rec_mode == FM_MODE)disp_fm_freq(); if(rec_mode == AM_MODE)disp_am_freq(); disp_volume(); disp_rec_mode(); setup_timer_0(T0_INTERNAL | T0_DIV_64 | T0_8_BIT); set_timer0(7); /* タイマー間隔は 64MHz/4/32/(256+1-7)=1000Hz 8 Bit mode */ enable_interrupts(INT_TIMER0); enable_interrupts(GLOBAL); old_volume = 100; while(1){ if(mute_func){ if(current_snr < snr_level){ set_volume(0); old_volume = 0; output_low(MUTE_LED); } else { if(old_volume != volume){ set_volume(volume); disp_volume(); } output_high(MUTE_LED); } } else{ if(old_volume != volume){ set_volume(volume); disp_volume(); } output_high(MUTE_LED); } if(sw1_status){ if(rec_mode==FM_MODE){ rec_mode=AM_MODE; restart_config(); } else{ rec_mode = FM_MODE; restart_config(); } common_clear(); disp_rec_mode(); if(rec_mode==FM_MODE){ disp_fm_freq(); disp_fm_freq_sled4(); } if(rec_mode==AM_MODE){ disp_am_freq(); disp_am_freq_sled4(); } disp_volume(); sw1_status = 0; } if(sw2_status && rec_mode==FM_MODE){ if(current_mode == NORMAL_MODE){ if(mute_func) mute_func = 0; else mute_func = 1; sw2_status=0; } else { current_mode = NORMAL_MODE; restart_config(); old_fm_freq = old_fm_freq = 0; sw2_status=0; } } else sw2_status=0; if(sw2_long_push && rec_mode==FM_MODE){ rec_mode = FM_MODE; restart_config(); set_fm_freq(); current_mode = SNRSET_MODE; sw2_long_push = 0; sw2_status=0; } else sw2_long_push = 0; if(current_mode == NORMAL_MODE){ if(rec_mode==FM_MODE){ if(old_fm_freq != fm_freq){ set_fm_freq(); disp_fm_freq(); disp_fm_freq_sled4(); old_fm_freq = fm_freq; } } if(rec_mode==AM_MODE){ if(old_am_freq != am_freq){ set_am_freq(); disp_am_freq(); disp_am_freq_sled4(); old_am_freq = am_freq; } } } else { disp_snr_level_sled4(); } if(start_count<4000){ if((start_count % 100)==0){ if(rec_mode==FM_MODE) disp_fm_freq_sled4(); if(rec_mode==AM_MODE) disp_am_freq_sled4(); } } disp_rssi(); disp_snr(); } } /* end of file */