/************************************************** * Memory Keyer Program * * Written for issue Sept. 2014 of Monthly FB News * * T.Tsujioka (JH1NRR/JL3YMC) * **************************************************/ // Last updated: 2019/12/18 // HISTORY: // 2014/09/01: Xon/Xoffフロー制御を追加しました。 // 2014/09/01: 行末にBTバーを自動挿入しないように仕様を変更しました。 // 2014/09/01: 先行入力については1行だけ編集を許可し、2行目は表示自体を待機させるようにしました。 // 2019/12/18: PLIBを使用しないようにし、MPLAB Xでコンパイルできるようにしました。 #include #include #define _XTAL_FREQ 48000000 // PLL clock of 48MHz // #define _XTAL_FREQ 20000000 // External clock of 20MHz #define Delay10TCYx(x) _delay((x) * 10) #define ENABLE_FLOWCONTROL // Xon/Xoffフロー制御を有効にする #define OUT_LED LATB7 #define OUT_KEY LATA5 #define KEY_DOT PORTCbits.RC0 #define KEY_DASH PORTCbits.RC5 #define KEY_RB0 PORTBbits.RB0 #define KEY_RB1 PORTBbits.RB1 #define KEY_RB2 PORTBbits.RB2 #define KEY_RB3 PORTBbits.RB3 #define KEY_REC PORTBbits.RB4 #define KEY_REPEAT PORTBbits.RB5 #define KEY_CH_SHIFT 1 // if not implemented // #define KEY_CH_SHIFT PORTAbits.RA4 // ■ トグルスイッチを実装したらこちらを有効にして下さい。 #pragma config PLLDIV = 5 // Divide by 5 (20 MHz oscillator input) #pragma config CPUDIV = OSC1_PLL2 // 96 MHz PLL Src: /2 #pragma config USBDIV = 2 // USB clock source comes from the 96 MHz PLL divided by 2 #pragma config FOSC = HSPLL_HS // HS oscillator, PLL enabled, HS used by USB #pragma config FCMEN = OFF // Fail-Safe Clock Monitor disabled #pragma config IESO = OFF // Oscillator Switchover mode disabled #pragma config PWRT = OFF // PoWeR-up Timer disabled #pragma config BOR = ON // Brown-out Reset enabled in hardware only (SBOREN is disabled) #pragma config BORV = 3 // Minimum setting #pragma config VREGEN = OFF // USB voltage regulator disabled (RC4,RC5) #pragma config WDT = OFF // HW Disabled - SW Controlled #pragma config WDTPS = 32768 // Watch-dog Timer Postscale Select: 1:32768 #pragma config MCLRE = ON // MCLR pin enabled; RE3 input pin disabled #pragma config LPT1OSC = OFF // Timer1 configured for higher power operation #pragma config PBADEN = ON // PORTB<4:0> pins are configured as analog input channels on Reset #pragma config CCP2MX = OFF // CCP2 input/output is multiplexed with RB3 #pragma config STVREN = ON // Stack full/underflow will cause Reset #pragma config LVP = OFF // Single-Supply ICSP disabled #pragma config XINST = OFF // Instruction set extension and Indexed Addressing mode disabled (Legacy mode) #pragma config DEBUG = OFF // Background debugger disabled, RB6 and RB7 configured as GPIO pins // ■現行のPICではデフォルトでプロテクトされていませんので、下記の設定は特には不要です。 // #pragma config CP3 = OFF // default: not protected // #pragma config WRT3 = OFF // default: not protected // #pragma config EBTR3 = OFF // default: not protected // Prototypes void pc_control_thread(void); // ===== RING BUFFER ===== #define RINGBUFSIZE 32 struct ringbuf_s { int rp, wp; int size; char rbuf[RINGBUFSIZE]; }; struct ringbuf_s txbuf, rxbuf; #ifdef ENABLE_FLOWCONTROL #define RINGBUFSIZE_LIMIT 26 // このサイズを超えるとXOFF(CTRL-S:一時停止)を送信 #define RINGBUFSIZE_SAFE 8 // このサイズを下回るとXON(CTRL-Q:再開)を送信 char tx_stop = 0, rx_stop = 0; char tx_ctrls = 0; #endif void ringbuf_clear(struct ringbuf_s *r) { r->rp = r->wp = 0; r->size = 0; } void ringbuf_write(struct ringbuf_s *r, char c) { if (r->size < RINGBUFSIZE - 1) { r->rbuf[r->wp] = c; // GIE = 0; if (++r->wp >= RINGBUFSIZE) { r->wp = 0; } // GIE = 1; r->size++; } else { // buffer overflow, ignored. } } char ringbuf_read(struct ringbuf_s *r) { char c; while (r->rp == r->wp); c = r->rbuf[r->rp]; // GIE = 0; if (++r->rp >= RINGBUFSIZE) { r->rp = 0; } // GIE = 1; r->size--; return (c); } char ringbuf_isempty(struct ringbuf_s *r) { return (r->rp == r->wp); // return (r->size > 0); にすると1バイト増える } int ringbuf_size(struct ringbuf_s *r) { return (r->size); } // ===== INTERRUPT HANDLER ===== void int_handler(void); void interrupt high_priority isr_high(void) { int_handler(); } void interrupt low_priority isr_low(void) { int_handler(); } int tick = 0; void int_handler(void) { char c; if (TMR0IE && TMR0IF) { TMR0 = 0; tick++; TMR0IF = 0; } if (TMR1IE && TMR1IF) { // Do nothing TMR1IF = 0; } if (CCP1IE && CCP1IF) { CCP1 = !CCP1; CCP1IF = 0; // set to 0 automatically } if (RCIE && RCIF) { #ifdef ENABLE_FLOWCONTROL c = RCREG; if (c == 19) { // Recv. CTRL-S tx_stop = 0; } else if (c == 17) { // Recv. CTRL-Q tx_stop = 1; } else { ringbuf_write(&rxbuf, c); if (ringbuf_size(&rxbuf) >= RINGBUFSIZE_LIMIT) { rx_stop = 1; // Send CTRL-S tx_ctrls = 1; if (TXIE == 0) { TXIE = 1; } } } #else c = RCREG; ringbuf_write(&rxbuf, c); #endif RCIF = 0; // 自動的にRCIF=0になるので、なくても良い } if (TXIE && TXIF) { #ifdef ENABLE_FLOWCONTROL if (tx_ctrls) { TXREG = 19; // Send CTRL-S tx_ctrls = 0; } else #endif if (ringbuf_isempty(&txbuf)) { TXIE = 0; } else { TXREG = ringbuf_read(&txbuf); TXIF = 0; // 無くてもよい } } } // ===== USART ===== void usart_tx(char c) { #ifdef ENABLE_FLOWCONTROL while (tx_stop) { // Wait until tx_flow is enabled } #endif while (ringbuf_size(&txbuf) >= RINGBUFSIZE - 2) { // Wait until buffer full is resolved } ringbuf_write(&txbuf, c); if (TXIE == 0) { TXIE = 1; } } void usart_tx_string(const char *p) { while (*p) { usart_tx(*p++); } } char usart_rx(void) { #ifdef ENABLE_FLOWCONTROL if (rx_stop && rxbuf.size < RINGBUFSIZE_SAFE) { usart_tx(17); // Send CTRL-Q rx_stop = 0; } #endif return (ringbuf_read(&rxbuf)); } char usart_rxisempty(void) { return (ringbuf_isempty(&rxbuf)); } void init_usart(void) { TXIE = 0; RCIE = 0; TXIP = 1; RCIP = 1; SPBRGH = 0; SPBRG = 77; TXSTA = 0b00100000; // 8bit, TXEN=1, SYNC=0(Async mode), BRGH=0 RCSTA = 0b10010000; // SPEN=1, 8bit, SREN=0, CREN=1 BAUDCON = 0b01000000; // ABDOVF=0, RCIDL=1, RXDTP=0, TXCKP=0, BRG16=0 ringbuf_clear(&txbuf); ringbuf_clear(&rxbuf); #ifdef ENABLE_FLOWCONTROL tx_stop = rx_stop = 0; tx_ctrls = 0; #endif RCIE = 1; } // ===== TIMER0 ===== void init_timer0(void) { // Tick timer (unused) TMR0 = 0; // 48MHz/4 / 32 / (256 + 1) = 1.459kHz T0CON = 0b11000100; // TMR0ON=1, T08BIT=1, T0CS=0, T0SE=0, PSA=0, T0PS=0b100(1:32) RCONbits.IPEN = 0; TMR0IE = 1; PEIE = 1; GIE = 1; } // ===== COMPARE1 ===== void init_compare(void) { #if 0 // cannot be used // freq. > 700Hz (not valid because of 8-bit counter) // OpenTimer2(TIMER_INT_OFF & T2_PS_1_16 & T2_POST_1_1); // OpenPWM1(446); // T = 3.2us * (445+1) = 1424us // SetDCPWM1(312); // T_high = 0.8us * 892 = 713.6us #endif #if 1 // 600Hz: 12MHz/600Hz/2=10000, T1_CCP1_T3_CCP2 // 700Hz: 12MHz/700Hz/2=8572, T1_CCP1_T3_CCP2 T3CCP2 = 0; T3CCP1 = 0; // T3CCP2:T3CCP1 = 0b00; TMR1 = 0; T1CON = 0b11000001; // RD16=1, T1RUN=1, T1CKPS=1:1, T1SYNC=0 TMR1CS=0, TMR1ON=1 CCP1CON = 0b00001011; // Compare mode: trigger special event, reset timer, start A/D conversion on CCP2 match CCPR1 = 10000; // ■ トーン周波数の600Hzを変更する場合はこの値を変えて下さい。12MHz÷周波数÷2で計算して下さい。 #endif } // ===== ADC ===== void init_adc(void) { // FOSC_16(<22MHz), FOSC_64(<48MHz) (TAD = 0.7us - 25us, Vref>=3.0) // T_ACQ >= 2.45us ADCON0 = 0b00000001; // Channel 0(AN0), ADON=1 ADCON1 = 0b00001001; // VREF-=VSS,VREF+=VDD,PCFG=0b1001 ADCON2 = 0b10110110; // ADFM=1, ACQT=16TAD,ADCS=FOSC/64 Delay10TCYx(5); } // ===== TONE / DELAY ===== int count_dash = 210; int count_dot = 70; int count_dot_silence = 70; double dash_dot_ratio = 3.0; void update_speed(void) { int adc_value_speed; int adc_value_ratio; ADCON0bits.CHS = 0; // Channel 0 (AN0) ADCON0bits.GO = 1; // start conversion while (ADCON0bits.nDONE); adc_value_speed = ADRES; Delay10TCYx(10); ADCON0bits.CHS = 1; // Channel 1 (AN1) ADCON0bits.GO = 1; // start conversion while (ADCON0bits.nDONE); adc_value_ratio = ADRES; Delay10TCYx(10); // ratio: 1:2.5 - 1:4.5 dash_dot_ratio = 3.5 + ((adc_value_ratio - 512.0) / 512.0); // count_dash: 100 - 356 count_dash = 100 + (1023 - adc_value_speed) / 4; count_dot = (int)(count_dash / dash_dot_ratio); #if 0 // ■ どちらか選んで下さい。短点スペースも短くするときは1、1:3を維持する場合は0です。 count_dot_silence = count_dot; #else count_dot_silence = count_dash / 3; #endif } void delay_dot(void) { int i; // The dot duration is the basic unit of time measurement in code transmission. for (i = 0; i < count_dot; i++) { __delay_ms(1); } } void delay_dot_silence(void) { int i; // Each dash or dot is followed by a short silence which is equal to a dot duration time. for (i = 0; i < count_dot_silence; i++) { __delay_ms(1); } } void delay_dash(void) { int i; // The duration of a dash is three times the duration of a dot. for (i = 0; i < count_dash; i++) { __delay_ms(1); } } void delay_half_dot(void) { int i; for (i = count_dot / 2; i > 0; i--) { __delay_ms(1); } } void delay_word_space(void) { int i; // The words are separated by a space equal to seven dots; thus a short silence plus two dash spaces. delay_dash(); delay_dash(); } void tone_on(void) { OUT_KEY = 1; CCP1IE = 1; } void tone_off(void) { OUT_KEY = 0; CCP1IE = 0; CCP1 = 0; } void tone_dash(void) { tone_on(); delay_dash(); tone_off(); delay_dot_silence(); // a short silence } void tone_dot(void) { tone_on(); delay_dot(); tone_off(); delay_dot_silence(); // a short silence } // ===== KEY ===== unsigned char is_anykeyon(void) { // Sense any key is on. if (KEY_RB0 == 0 || KEY_RB1 == 0 || KEY_RB2 == 0 || KEY_RB3 == 0 || KEY_DOT == 0 || KEY_DASH == 0 || KEY_REPEAT == 0 || KEY_REC == 0) { return (1); } else { return (0); } } unsigned char is_anykeyon_wo_dotdash(void) { // Sense any key is on. if (KEY_RB0 == 0 || KEY_RB1 == 0 || KEY_RB2 == 0 || KEY_RB3 == 0 || KEY_REPEAT == 0 || KEY_REC == 0) { return (1); } else { return (0); } } void wait_keysoff(void) { // Wait for all keys released. while (is_anykeyon()) { delay_dot(); } } void wait_keysoff_wo_dotdash(void) { // Wait for all keys released. while (is_anykeyon_wo_dotdash()) { delay_dot(); } } // ===== MORSE CONVERSION ===== #define M_DOT 1 #define M_DASH 2 #define M_SPACE 3 #define M_WSPACE 4 #define M_END 0 struct morse_s { char c; char code[8]; }; struct morse_s morse[] = { { 'A', { M_DOT, M_DASH, M_END } }, { 'B', { M_DASH, M_DOT, M_DOT, M_DOT, M_END } }, { 'C', { M_DASH, M_DOT, M_DASH, M_DOT, M_END } }, { 'D', { M_DASH, M_DOT, M_DOT, M_END } }, { 'E', { M_DOT, M_END } }, { 'F', { M_DOT, M_DOT, M_DASH, M_DOT, M_END } }, { 'G', { M_DASH, M_DASH, M_DOT, M_END } }, { 'H', { M_DOT, M_DOT, M_DOT, M_DOT, M_END } }, { 'I', { M_DOT, M_DOT, M_END } }, { 'J', { M_DOT, M_DASH, M_DASH, M_DASH, M_END } }, { 'K', { M_DASH, M_DOT, M_DASH, M_END } }, { 'L', { M_DOT, M_DASH, M_DOT, M_DOT, M_END } }, { 'M', { M_DASH, M_DASH, M_END } }, { 'N', { M_DASH, M_DOT, M_END } }, { 'O', { M_DASH, M_DASH, M_DASH, M_END } }, { 'P', { M_DOT, M_DASH, M_DASH, M_DOT, M_END } }, { 'Q', { M_DASH, M_DASH, M_DOT, M_DASH, M_END } }, { 'R', { M_DOT, M_DASH, M_DOT, M_END } }, { 'S', { M_DOT, M_DOT, M_DOT, M_END } }, { 'T', { M_DASH, M_END } }, { 'U', { M_DOT, M_DOT, M_DASH, M_END } }, { 'V', { M_DOT, M_DOT, M_DOT, M_DASH, M_END } }, { 'W', { M_DOT, M_DASH, M_DASH, M_END } }, { 'X', { M_DASH, M_DOT, M_DOT, M_DASH, M_END } }, { 'Y', { M_DASH, M_DOT, M_DASH, M_DASH, M_END } }, { 'Z', { M_DASH, M_DASH, M_DOT, M_DOT, M_END } }, { '0', { M_DASH, M_DASH, M_DASH, M_DASH, M_DASH, M_END } }, { '1', { M_DOT, M_DASH, M_DASH, M_DASH, M_DASH, M_END } }, { '2', { M_DOT, M_DOT, M_DASH, M_DASH, M_DASH, M_END } }, { '3', { M_DOT, M_DOT, M_DOT, M_DASH, M_DASH, M_END } }, { '4', { M_DOT, M_DOT, M_DOT, M_DOT, M_DASH, M_END } }, { '5', { M_DOT, M_DOT, M_DOT, M_DOT, M_DOT, M_END } }, { '6', { M_DASH, M_DOT, M_DOT, M_DOT, M_DOT, M_END } }, { '7', { M_DASH, M_DASH, M_DOT, M_DOT, M_DOT, M_END } }, { '8', { M_DASH, M_DASH, M_DASH, M_DOT, M_DOT, M_END } }, { '9', { M_DASH, M_DASH, M_DASH, M_DASH, M_DOT, M_END } }, // { '.', { M_DOT, M_DASH, M_DOT, M_DASH, M_DOT, M_DASH, M_END } }, // ~AAA { ',', { M_DASH, M_DASH, M_DOT, M_DOT, M_DASH, M_DASH, M_END } }, // ~MIM { '?', { M_DOT, M_DOT, M_DASH, M_DASH, M_DOT, M_DOT, M_END } }, // ~IMI { '!', { M_DOT, M_DOT, M_DOT, M_DASH, M_DOT, M_END } }, // ~SN { '-', { M_DASH, M_DOT, M_DOT, M_DOT, M_DOT, M_DASH, M_END } }, // ~DU { '/', { M_DASH, M_DOT, M_DOT, M_DASH, M_DOT, M_END } }, // ~DN { '@', { M_DOT, M_DASH, M_DASH, M_DOT, M_DASH, M_DOT, M_END } }, // ~PN ~WR { '(', { M_DASH, M_DOT, M_DASH, M_DASH, M_DOT, M_END } }, // ~KN { ')', { M_DASH, M_DOT, M_DASH, M_DASH, M_DOT, M_DASH, M_END } }, // ~KK // { '+', { M_DOT, M_DASH, M_DOT, M_DASH, M_DOT, M_END } }, // ~AR { '$', { M_DOT, M_DOT, M_DOT, M_DASH, M_DOT, M_DOT, M_DASH, M_END } }, // ~SX { '%', { M_DASH, M_DOT, M_DASH, M_DOT, M_DASH, M_END } }, // ~KA { '&', { M_DOT, M_DASH, M_DOT, M_DOT, M_DOT, M_END } }, // ~AS { '\'',{ M_DOT, M_DASH, M_DASH, M_DASH, M_DASH, M_DOT, M_END } }, // ~WG { ':', { M_DASH, M_DASH, M_DASH, M_DOT, M_DOT, M_DOT, M_END } }, // ~OS { ';', { M_DASH, M_DOT, M_DASH, M_DOT, M_DASH, M_DOT, M_END } }, // ~KR { '=', { M_DASH, M_DOT, M_DOT, M_DOT, M_DASH, M_END } }, // ~BT { '|', { M_DOT, M_DOT, M_DOT, M_DASH, M_DOT, M_DASH, M_END } }, // ~SK { '\\',{ M_DOT, M_DASH, M_DOT, M_DASH, M_DOT, M_DOT, M_END } }, // ~AL { '_', { M_DOT, M_DOT, M_DASH, M_DASH, M_DOT, M_DASH, M_END } }, // ~IQ // // { '\r', { M_DASH, M_DOT, M_DOT, M_DOT, M_DASH, M_END } }, // BT BAR // 前後のSPも指定しなければならないのでこの方法は止めました // { '\n', { M_DASH, M_DOT, M_DOT, M_DOT, M_DASH, M_END } }, // BT BAR // 同上 { '\0', { M_END } }, }; unsigned char morse_toupper(unsigned c) { if (c >= 'a' && c <= 'z') { c -= 0x20; } return (c); } char scan_morse(char c) { char i; c = morse_toupper(c); for (i = 0; morse[i].c; i++) { if (morse[i].c == c) { return (i); } } return (-1); } void morse_conversion(char *m, const unsigned char *string) { int i, j; unsigned char c; unsigned char *p; unsigned char bar_flag; p = (unsigned char *)string; bar_flag = 0; while (c = *p++) { if (c == '\\' || c == '~') { // BTバーなどは ~BT または \\BT と入力します。 bar_flag = 1; } else if (c == ' ') { // Word space... *m++ = M_WSPACE; bar_flag = 0; } else if (c == '\r' || c == '#') { // \rまたは#でBTバー挿入 #if 1 // \rまたは#は前後にSPが挿入された~BT(文区切り)に変換して挿入します。 *m++ = M_WSPACE; *m++ = M_DASH; *m++ = M_DOT; *m++ = M_DOT; *m++ = M_DOT; *m++ = M_DASH; *m++ = M_WSPACE; #endif bar_flag = 0; } else { // Roman letters and numbers... i = scan_morse(c); if (i >= 0) { for (j = 0; morse[i].code[j]; j++) { *m++ = morse[i].code[j]; } if (!bar_flag) { *m++ = M_SPACE; // character separator } } } } *m = M_END; } // ===== MORSE MEMORY ===== #define MAXCH 8 #define MAXMEMLEN 1024 #define FLASH_ADDRESS 0x6000 #define FLASH_ERASE_BLOCK 64 #define FLASH_WRITE_BLOCK 32 unsigned long mem_tblptr[MAXCH] = { FLASH_ADDRESS, FLASH_ADDRESS + MAXMEMLEN, FLASH_ADDRESS + MAXMEMLEN * 2, FLASH_ADDRESS + MAXMEMLEN * 3, FLASH_ADDRESS + MAXMEMLEN * 4, FLASH_ADDRESS + MAXMEMLEN * 5, FLASH_ADDRESS + MAXMEMLEN * 6, FLASH_ADDRESS + MAXMEMLEN * 7 }; char mem[MAXMEMLEN]; void read_mem(unsigned char raw_ch, char *p) { int i; unsigned long address; address = mem_tblptr[raw_ch]; TBLPTR = address; for (i = 0; i < MAXMEMLEN; i++) { asm("TBLRD*+"); mem[i] = TABLAT; } } void write_mem(unsigned char raw_ch, char *p) { int i; unsigned long address; address = mem_tblptr[raw_ch]; for (i = 0; i < MAXMEMLEN; i += FLASH_ERASE_BLOCK) { int j, k; TBLPTR = address + i; EECON1bits.EEPGD = 1; // Access Flash program memory EECON1bits.CFGS = 0; // Access Flash program memory EECON1bits.WREN = 1; // Write Enable EECON1bits.FREE = 1; // Erase program memory row addressed by TBLPTR GIE = 0; EECON2 = 0x55; EECON2 = 0xaa; EECON1bits.WR = 1; // Start erase (CPU stall) while (EECON1bits.WR); // wait until WR=0 (auto-cleared) GIE = 1; for (j = 0; j < FLASH_ERASE_BLOCK; j += FLASH_WRITE_BLOCK) { TBLPTR = address + i + j; for (k = 0; k < FLASH_WRITE_BLOCK; k++) { TABLAT = *p++; asm("TBLWT*+"); } TBLPTR = address + i + j; // TBLPTR must be in the 32-byte write block EECON1bits.EEPGD = 1; // Access Flash program memory EECON1bits.CFGS = 0; // Access Flash program memory EECON1bits.WREN = 1; // Write Enable EECON1bits.FREE = 0; // Perform write-only GIE = 0; EECON2 = 0x55; EECON2 = 0xaa; EECON1bits.WR = 1; // Start program (CPU stall) while (EECON1bits.WR); // wait until WR=0 (auto-cleared) GIE = 1; } EECON1bits.WREN = 0; // Write Disable } } void init_mem_default(void) { // ■RECスイッチを押しながら電源を入れた時に設定される定型文を、見本に従って設定して下さい。 morse_conversion(mem, "CQ CQ CQ DE JL3YMC JL3YMC JL3YMC PSE ~AR"); write_mem(0, mem); morse_conversion(mem, "DE JL3YMC JL3YMC ~AR"); write_mem(1, mem); morse_conversion(mem, "TNX FB QSO ~BT MY QSL VIA JARL"); write_mem(2, mem); morse_conversion(mem, "CUAGN 73 ~SK E E"); write_mem(3, mem); morse_conversion(mem, ""); write_mem(4, mem); write_mem(5, mem); write_mem(6, mem); write_mem(7, mem); } // ===== PLAY BACK ===== #define REPEAT_LIMIT 100 // ■この回数以上はリピートしません(送信事故を回避するため) #define REPEAT_BLANK 15 // ■リピート時に間に挿入するブランク長です。ボリュームで調整できるようにすると良いかもしれません。 void playback_mem(int repeat_times) { int i, j; char c; // Wait until all keys are off. wait_keysoff(); OUT_LED = 1; // Pleyback /w repeat for (i = 0; i < repeat_times; i++) { // A long silence for repeat transmission if (i > 0) { for (j = 0; j < REPEAT_BLANK; j++) { delay_dash(); #if 1 // Break if any key is on. if (is_anykeyon()) { delay_dot(); // delay_dash(); OUT_LED = 0; return; } #endif } } // Playback from memory for (j = 0; j < MAXMEMLEN; j++) { c = mem[j]; if (c == M_END) break; switch (c) { case M_DOT: tone_dot(); break; case M_DASH: tone_dash(); break; case M_SPACE: delay_dash(); break; case M_WSPACE: delay_word_space(); break; } #if 1 // Break if any key is on. if (is_anykeyon()) { delay_dot(); // delay_dash(); // wait_keysoff(); // comment this out if necessary OUT_LED = 0; return; } #endif // PC CONTROL pc_control_thread(); #if 1 // UPDATE SPEED & RATIO update_speed(); #endif } } OUT_LED = 0; } void playback(unsigned char ch) { int repeat_times; // Set repeat times if (KEY_REPEAT == 0) { repeat_times = REPEAT_LIMIT; } else { repeat_times = 1; } // Channel bank if (KEY_CH_SHIFT == 0) { ch += 4; } // Read from Flash read_mem(ch, mem); // Playback playback_mem(repeat_times); } // ===== RECORD ===== void record(unsigned char ch) { int i, j; int count_space; char c; // Wait until all keys are off. wait_keysoff(); OUT_LED = 1; i = 0; count_space = 0; while (1) { if (KEY_DASH != 0 && KEY_DOT != 0) { delay_half_dot(); count_space++; } else { if (i > 0) { if (count_space <= 1) { // ignored } else if (count_space <= 8) { // Character (Letter or Number) space... mem[i++] = M_SPACE; } else { // Word space... #if 0 // ■どちらか選んで下さい。ブランク長を記録するときは1、固定値挿入の場合は0です。 j = (int)(count_space / (dash_dot_ratio * 2.0) + 0.0); #else j = 1; #endif for (; j > 0; j--) { mem[i++] = M_WSPACE; } } } if (KEY_DASH == 0) { tone_dash(); mem[i++] = M_DASH; count_space = 0; } if (KEY_DOT == 0) { tone_dot(); mem[i++] = M_DOT; count_space = 0; } } if (KEY_REC == 0) break; #if 1 // Cancel if any key is on except REC key. if (is_anykeyon_wo_dotdash()) { delay_dot(); // delay_dash(); // wait_keysoff(); // comment this out if necessary OUT_LED = 0; return; } #endif #if 1 // UPDATE SPEED & RATIO update_speed(); #endif } mem[i] = M_END; // Channel bank if (KEY_CH_SHIFT == 0) { ch += 4; } // Write to Flash write_mem(ch, mem); OUT_LED = 0; } // ===== PC CONTROL ===== #define MAXLINEBUFSIZE 128 char linebuf[MAXLINEBUFSIZE]; int linebufsize = 0; int lastlinesize = 0; void pc_command(void) { char ch; char *p; if (linebuf[0] == '!') { // --- Playback from memory --- usart_tx_string("Sending...\r\n"); ch = (linebuf[1] - '1') % MAXCH; playback(ch); delay_word_space(); // 行間スペース } else if (linebuf[0] == '%') { // --- Load into memory --- usart_tx_string("Loaded & Sending...\r\n"); ch = (linebuf[1] - '1') % MAXCH; p = &linebuf[2]; while (*p == ' ') p++; // skip space morse_conversion(mem, p); write_mem(ch, mem); playback_mem(1); // ■登録時も1回再生 delay_word_space(); // 行間スペース } else { // --- Playback from linebuf --- usart_tx_string("Sending...\r\n"); p = &linebuf[0]; while (*p == ' ') p++; // skip space morse_conversion(mem, p); playback_mem(1); delay_word_space(); // 行間スペース } } char pc_exec_flag = 0; char in_pc_command = 0; void pc_control_execute(void) { if (in_pc_command) return; while (pc_exec_flag) { linebuf[linebufsize] = '\0'; linebufsize = 0; pc_exec_flag = 0; in_pc_command = 1; pc_command(); in_pc_command = 0; } } void pc_control_thread(void) { char c; if (in_pc_command && pc_exec_flag) { // 先行入力は1行だけとする.2行先のときはpauseする. return; } while (!usart_rxisempty()) { c = usart_rx(); if (c == '\b') { // バックスペース処理 usart_tx('\b'); usart_tx(0x1b); usart_tx('['); usart_tx('X'); if (lastlinesize > 0) { linebufsize--; lastlinesize--; } } else if (c == '\r') { // 改行処理 usart_tx('\r'); // CR usart_tx('\n'); // LF if (linebufsize > 0) { pc_exec_flag = 1; lastlinesize = 0; } else { // 空白行をスキップ } break; // whileループを一旦抜ける(1行ごとに処理する) } else { // ラインバッファに1文字を追加 if (linebufsize < MAXLINEBUFSIZE - 1) { usart_tx(c); linebuf[linebufsize++] = c; lastlinesize++; } else { // line buffer overflow, ignored } } } } // ===== MAIN ===== void main(void) { OSCCON = 0b00110000; // Internal 8MHz: unused ADCON1 = 0b00001101; // DDDDDDAA: AN1-0:enable INTCON = 0; INTCON2 = 0b01110101; // RB Pull-Up=on, TMR0IP=high // CMCON = 0x07; // To use pins RC4 and RC5 as digital inputs, the USB module must be disabled (UCON<3> = 0) // and the on-chip USB transceiver must be disabled(UCFG<3> = 1). UCON = 0b00000000; // UCON.USBEN = 0; UCFG = 0b00001000; // UCFG.UTRDIS = 1; TRISA = 0b00000011; // RA1-0=AN1-AN0 TRISB = 0b00111111; // RB7-6=out, RB5-1:inp TRISC = 0b10110001; // RC7,RC5,RC0=inp init_timer0(); init_compare(); init_adc(); init_usart(); // Initialize morse memory, if requested if (KEY_REC == 0) { init_mem_default(); } // Sound off tone_off(); // LED off OUT_LED = 0; // Main loop while (1) { // DOT if (KEY_DOT == 0) { tone_dot(); } // DASH if (KEY_DASH == 0) { tone_dash(); } // RECORD / PLAYBACK if (KEY_REC == 0) { if (KEY_RB3 == 0) { record(0); } else if (KEY_RB2 == 0) { record(1); } else if (KEY_RB1 == 0) { record(2); } else if (KEY_RB0 == 0) { record(3); } } else { if (KEY_RB3 == 0) { playback(0); } else if (KEY_RB2 == 0) { playback(1); } else if (KEY_RB1 == 0) { playback(2); } else if (KEY_RB0 == 0) { playback(3); } } // PC CONTROL pc_control_thread(); pc_control_execute(); #if 1 // UPDATE SPEED & RATIO update_speed(); #endif } }