Tasteneingänge. Muss ein > 100€ Keyer überhaupt eine Entprellung haben? Überlegung, ob es nicht sinnvoller ist, die Tasteneingänge per Interrupt abzufragen. Das würde den Code auch noch erheblich vereinfachen, da nicht ständig an allen möglichen Stellen die Paddle abgefragt werden müssten.
		
			
				
	
	
		
			383 Zeilen
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			383 Zeilen
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @brief  BJ-Keyer
 | |
| 
 | |
|             Morsekeyer von DL7BJ
 | |
|             tom@dl7bj.de
 | |
| 
 | |
|             OLED functions from https://github.com/Sylaina/oled-display
 | |
| 
 | |
| @verbatim
 | |
|  History
 | |
|  --------------------------------------------------------------------------
 | |
|  2012-05-24 DL7BJ	erste Version
 | |
|  2013-05-10 DL7BJ	Generierung des Mithörtons als Sinus mit PWM/DDS
 | |
|  2013-07-15 DL7BJ	Änderungen der Keyerfunktionen
 | |
|  2013-07-19 DL7BJ	Beep/Boop (Spielkram)
 | |
|  2013-10-20 DL7BJ   Änderungen der PWM Funktionen für besseren Sinus
 | |
|  2022-04-10 DL7BJ   erste Leiterplatten für Prototyp (bisher Lochraster)
 | |
|  2022-09-02 DL7BJ   viele Softwareänderungen, neuer Filter für PWM
 | |
|  2022-09-11 DL7BJ   Encoder, LC-Display, Frontplatine "entsorgt"
 | |
|  2023-06-28 DL7BJ   Port Anpassungen an neue Leiterplatte V1.01
 | |
|  2023-07-29 DL7BJ   Menü für Einstellungen implementiert
 | |
|  2023-08-06 DL7BJ   Quelltexte umstrukturiert, Aufteilung
 | |
| 
 | |
|  ATMEGA328(P)
 | |
|                             ----------
 | |
|  (PCINT14/_RESET)     PC6 -| 1      28|- PC5 (ADC5/SCL/PCINT13)
 | |
|  (PCINT16/RXD)        PD0 -| 2      27|- PC4 (ADC4/SDA/PCINT12)
 | |
|  (PCINT17/TXT)        PD1 -| 3      26|- PC3 (ADC3/PCINT11)
 | |
|  (PCINT18/INT0)       PD2 -| 4      25|- PC2 (ADC2/PCINT10)
 | |
|  (PCINT19/OC2B/INT1)  PD3 -| 5      24|- PC1 (ADC1/PCINT9)
 | |
|  (PCINT20/XCK/T0)     PD4 -| 6      23|- PC0 (ADC0/PCINT8)
 | |
|                       VCC -| 7      22|- GND
 | |
|                       GND -| 8      21|- AREF
 | |
|  (PCINT6/XTAL1/TOSC1) PB6 -| 9      20|- AVCC
 | |
|  (PCINT7/XTAL2/TOSC2) PB7 -|10      19|- PB5 (SCK/PCINT5)
 | |
|  (PCINT21/OC0B/T1)    PD5 -|11      18|- PB4 (MISO/PCINT4)
 | |
|  (PCINT22/OC0A/AIN0)  PD6 -|12      17|- PB3 (MOSI/OC2A/PCINT3)
 | |
|  (PCINT23/AIN1)       PD7 -|13      16|- PB2 (SS/OC1B/PCINT2)
 | |
|  (PCINT0/CLK0/ICP1)   PB0 -|14      15|- PB1 (OC1A/PCINT1)
 | |
|                             ----------
 | |
| 
 | |
|  Pin  1 - PC6  - Reset                  Pin 28 - PC5    - SCL Display
 | |
|  Pin  2 - PD0  - RxD                    Pin 27 - PC4    - SDA Display
 | |
|  Pin  3 - PD1  - TxD                    Pin 26 - PC3    - LED Key
 | |
|  Pin  4 - PD2  - Left Paddle            Pin 25 - PC2    - TRX 2 Out
 | |
|  Pin  5 - PD3  - Right Paddle           Pin 24 - PC1    - TRX 1 Out
 | |
|  Pin  6 - PD4  - Straight Key           Pin 23 - PC0    - Mem 4
 | |
|                                         Pin 19 - PB5    - Mem 5
 | |
|  Pin 11 - PD5  - Mem 1                  Pin 18 - PB4    - _Audio SD
 | |
|  Pin 12 - PD6  - Mem 2                  Pin 17 - OC2A   - Audio PWM output
 | |
|  Pin 13 - PD7  - Mem 3                  Pin 16 - PB2    - Encoder Switch
 | |
|  Pin 14 - PB0  - Encoder A              Pin 15 - PB1    - Encoder B
 | |
| 
 | |
| 
 | |
|  Value 1   2   4   8  32  64  128  255
 | |
|  Bit   1   2   3   4   5   6    7    8
 | |
|  Pin   0   1   2   3   4   5    6    7
 | |
| @endverbatim
 | |
| */
 | |
| 
 | |
| #include "main.h"
 | |
| 
 | |
| // Sinustabelle für PWM mit 64 Werten
 | |
| const unsigned char sinewave[] PROGMEM = {
 | |
|     0x00, 0x01, 0x02, 0x05, 0x0a, 0x0f, 0x15, 0x1d, 0x25, 0x2f, 0x39, 0x43, 0x4f, 0x5a, 0x67, 0x73, 
 | |
|     0x80, 0x8c, 0x98, 0xa5, 0xb0, 0xbc, 0xc6, 0xd0, 0xda, 0xe2, 0xea, 0xf0, 0xf5, 0xfa, 0xfd, 0xfe, 
 | |
|     0xff, 0xfe, 0xfd, 0xfa, 0xf5, 0xf0, 0xea, 0xe2, 0xda, 0xd0, 0xc6, 0xbc, 0xb0, 0xa5, 0x98, 0x8c, 
 | |
|     0x80, 0x73, 0x67, 0x5a, 0x4f, 0x43, 0x39, 0x2f, 0x25, 0x1d, 0x15, 0x0f, 0x0a, 0x05, 0x02, 0x01
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * \brief Initialsieren der Timer
 | |
|  *
 | |
|  * Alle Parameter der Timer basieren auf 16MHz Systemtakt.
 | |
|  *
 | |
|  * Timer 0  - 8 Bit timer für 1ms
 | |
|  * Timer 2  - 8 Bit timer für PWM zur Erzeugung des Sinustons
 | |
|  * Timer 1A - 16 Bit timer zur Erzeugung der Hüllkurve 
 | |
|  * Timer 1B - 16 Bit timer wird nicht benutzt
 | |
|  *
 | |
|  * T   - dot duration
 | |
|  * wpm - Words per Minute based on PARIS
 | |
|  * Formula T = 1200 / wpm
 | |
|  * Minimum speed 10 wpm - dot duration 120ms
 | |
|  * Maximum speed 99 wpm - dot duration 12ms
 | |
|  *
 | |
| */
 | |
| void InitTimer(void)
 | |
| {
 | |
|     cli();
 | |
|     // Timer 2 PWM
 | |
|     TCCR2A = 0;
 | |
|     TCCR2B = 0;
 | |
|     // No prescaling
 | |
|     sbi(TCCR2B,CS20);
 | |
|     // Clear OC2A on compare match
 | |
|     sbi(TCCR2A,COM2A1);
 | |
|     // Fast PWM Mode
 | |
|     sbi(TCCR2A,WGM20);
 | |
|     sbi(TCCR2A,WGM21);
 | |
|     // Phase Correct PWM
 | |
|     //sbi(TCCR2A,WGM22);
 | |
|     //sbi(TCCR2A,WGM20);
 | |
|     // Initial value
 | |
|     OCR2A = 0x80;
 | |
|     sbi(DDRB,PB3);
 | |
|     cbi(TIMSK2,OCIE0A);
 | |
| 
 | |
|     // Timer 1 für die Sinus Hüllkurve
 | |
|     TCCR1A = 0; TCCR1B = 0; TIMSK1 = 0; 
 | |
|     // CTC Mode
 | |
|     sbi(TCCR1B,WGM12);
 | |
|     // Prescaling 8
 | |
|     sbi(TCCR1B,CS11);
 | |
|     // Output Compare Match Interrupt Enable
 | |
|     OCR1A = 51;             // 600Hz
 | |
|     sbi(TIMSK1,OCIE1A);     // Timer 1 Sinus Hüllkurve einschalten
 | |
| 
 | |
|     // Timer 0 1ms für diverse Zähler
 | |
|     TCCR0A = 0; TCCR0B = 0; TCNT0 = 0;
 | |
|     cbi(TCCR0A,WGM00);
 | |
|     sbi(TCCR0A,WGM01);
 | |
|     cbi(TCCR0B,WGM02);      // CTC Mode 2 Immediate
 | |
|     cbi(TCCR0B,CS02);
 | |
|     sbi(TCCR0B,CS01);
 | |
|     sbi(TCCR0B,CS00);       // prescaler 64
 | |
|     OCR0A = 249;            // CTC 1ms
 | |
|     sbi(TIMSK0,OCIE0A);     // Timer 0 1ms einschalten
 | |
|     sei();
 | |
| }
 | |
| 
 | |
| void Init()
 | |
| {
 | |
|     cli(); // disable all interrupts
 | |
|     bState.SendStatus = SENDING_NOTHING;
 | |
|     // PORTB
 | |
|     DDRB = 0x00;
 | |
|     // Interne PullUps einschalten
 | |
|     sbi(PORTB,PB0);
 | |
|     sbi(PORTB,PB1);
 | |
|     sbi(PORTB,PB2);
 | |
|     sbi(PORTB,PB3);
 | |
|     sbi(PORTB,PB5);
 | |
|     sbi(PORTB,AUDIO_EN);
 | |
|     // Ein- und Ausgänge festlegen
 | |
|     sbi(DDRB,PB3);          // PWM
 | |
|     sbi(DDRB,AUDIO_EN);     // Audio Verstärker
 | |
|     // PORTC
 | |
|     sbi(DDRC,MORSE_LED);
 | |
|     // PORTD
 | |
|     // Ein- und Ausgänge festlegen
 | |
|     DDRD = 0x00;
 | |
|     // Interne PullUps für die Eingänge abschalten
 | |
|     cbi(PORTD,LEFT_PADDLE);
 | |
|     cbi(PORTD,RIGHT_PADDLE);
 | |
|     cbi(PORTD,STRAIGHT_KEY);
 | |
| 
 | |
|     LengthOfElement = (uint16_t)1200/bConfig.WpM;
 | |
| 
 | |
|     // Pin Change Interrupts Port D - Keys
 | |
|     // Alle Pins liegen im PCINT2 Vektor
 | |
|     // PD4 - StraightKey  - PCINT20 - Pin Change Interrupt 20
 | |
|     // PD3 - Right Paddle - PCINT19 - Pin Change Interrupt 19
 | |
|     // PD2 - Left Paddle  - PCINT18 - Pin Change Interrupt 18
 | |
|     // sbi(PCICR,PCIE2);
 | |
|     // sbi(PCMSK2,PCINT18);
 | |
|     // sbi(PCMSK2,PCINT19);
 | |
|     // sbi(PCMSK2,PCINT20);
 | |
| 
 | |
|     // Init serial
 | |
|     UBRR0=UBRR_VALUE;   // Set baud rate
 | |
|     sbi(UCSR0B,TXEN0);  // Enable TX
 | |
|     sbi(UCSR0B,RXEN0);  // Enable RX
 | |
|     sbi(UCSR0B,RXCIE0); // RX complete interrupt
 | |
|     sbi(UCSR0C,UCSZ01); // no parity, 1 stop bit
 | |
|     sbi(UCSR0C,UCSZ01); // 8-bit data
 | |
| 
 | |
|     InitTimer();
 | |
|     EncoderInit();
 | |
| 
 | |
|     // Initialisierung Menüvariablen
 | |
|     bMenuCtrl.ClrScr = 1;
 | |
|     bMenuCtrl.Update = 1;
 | |
|     bMenuCtrl.Config = 0;
 | |
|     bMenuCtrl.buttonPressed = 0;
 | |
|     bMenuCtrl.buttonPressedLong = 0;
 | |
|     bMenuCtrl.WriteEEprom = 0;
 | |
|     sei(); // enable all interrupts
 | |
| }
 | |
| 
 | |
| ISR(TIMER1_COMPA_vect)
 | |
| {
 | |
|     ocr2a = pgm_read_byte_near(sinewave+icnt);
 | |
|     icnt++;
 | |
|     if(StateRiseTime > 0)
 | |
|         OCR2A = (ocr2a >> StateRiseTime);
 | |
|     else
 | |
|         OCR2A = ocr2a;
 | |
|     
 | |
|     if(icnt == SINEWAVELENGTH)
 | |
|     {
 | |
|         icnt = 0;
 | |
|         if(bState.SidetoneOff == 0)
 | |
|         {
 | |
|             if(StateRiseTime > 0)
 | |
|             {
 | |
|                 StateRiseTimeCounter++;
 | |
|                 if(StateRiseTimeCounter > bConfig.RiseTimeCounter)
 | |
|                     StateRiseTime--;
 | |
|             }
 | |
|         }
 | |
|         if(bState.SidetoneOff == 1)
 | |
|         {
 | |
|             if(StateRiseTime < bConfig.RiseTime)
 | |
|             {
 | |
|                 StateRiseTimeCounter++;
 | |
|                 if(StateRiseTimeCounter > bConfig.RiseTimeCounter)
 | |
|                     StateRiseTime++;
 | |
|             } else {
 | |
|                 OCR2A = 0;
 | |
|                 cbi(TIMSK1,OCIE1A);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /** @brief 8 Bit Timer 0 ISR routine
 | |
|  *
 | |
|  *  The Timer 0 CTC interrupt will be fired every millisecond and 
 | |
|  *  has a bundle of counter variables for different timings.
 | |
|  *
 | |
|  *  params: none
 | |
|  *  return: none
 | |
|  *
 | |
| */
 | |
| ISR(TIMER0_COMPA_vect)
 | |
| {
 | |
|     t_delayms++;            // 16Bit counter for milliseconds
 | |
|     t_elementlength++;      // Length of element dit/dah
 | |
|     t_wait++;
 | |
|     t_timer++;             
 | |
|     StoreEEprom++;          // Zähler für Zeitablauf speichern EEprom
 | |
|     MenuCtrlTimer++;        // Zähler für Zeitablauf Einstellungen
 | |
|     EncoderTimer++;         // Zähler für 5ms Drehencoder Timer
 | |
| 
 | |
|     if(t_timer >= L_WAIT)
 | |
|     {
 | |
|         t_timer = 0;
 | |
|     }
 | |
|     // Alle 5ms den Drehencoder abfragen
 | |
|     if(EncoderTimer > 5) 
 | |
|     {
 | |
|         EncoderTimer = 0;
 | |
|         EncoderPolling();
 | |
|         // Schalter vom Drehencoder abfragen
 | |
|         lastButton = EncoderGetButtonState();
 | |
|         if(lastButton == ButtonPressed_Short)
 | |
|         {
 | |
|             bMenuCtrl.buttonPressed = 1;
 | |
|         }
 | |
|         if(lastButton == ButtonPressed_Long)
 | |
|         {
 | |
|             bMenuCtrl.buttonPressedLong = 1;
 | |
|             if(bMenuCtrl.Config == 1)
 | |
|                 bState.WriteEEprom = 1;
 | |
|         }
 | |
|     }
 | |
|     // WpM verändert? Nach 5 Sekunden im EEPROM Speichern
 | |
|     if((StoreEEprom > 1000) && (bState.WpMChanged))
 | |
|     {
 | |
|         bState.WriteWpMEEprom = 1;
 | |
|         bState.WpMChanged = 0;
 | |
|     }
 | |
|     // Softwareentprellung für StraightKey
 | |
|     TimerStraightKeyPressed++;
 | |
|     if(StateStraightKeyPressed == KEY_PRESSED_DEBOUNCE)
 | |
|     {
 | |
|         if(TimerStraightKeyPressed > bConfig.DebounceTime) 
 | |
|             StateStraightKeyPressed = KEY_PRESSED;
 | |
|     }
 | |
|     // Softwarentprellung für Paddle
 | |
|     TimerPaddleDitKeyPressed++;
 | |
|     if(StatePaddleDitKeyPressed == KEY_PRESSED_DEBOUNCE)
 | |
|     {
 | |
|         if(TimerPaddleDitKeyPressed > bConfig.DebounceTime)
 | |
|             StatePaddleDitKeyPressed = KEY_PRESSED;
 | |
|     }
 | |
|     // Softwarentprellung für Paddle
 | |
|     TimerPaddleDahKeyPressed++;
 | |
|     if(StatePaddleDahKeyPressed == KEY_PRESSED_DEBOUNCE)
 | |
|     {
 | |
|         if(TimerPaddleDahKeyPressed > bConfig.DebounceTime)
 | |
|             StatePaddleDahKeyPressed = KEY_PRESSED;
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| /** \brief 8 Bit Timer 2
 | |
|  *
 | |
|  * Timer 2 overflow interrupt
 | |
|  * Mit diesem Interrupt wird der nächste Wert für die
 | |
|  * Erzeugung des Sinus für den Mithörton geladen.
 | |
|  *
 | |
| */
 | |
| ISR(TIMER2_OVF_vect)
 | |
| {
 | |
|     /* nothing */
 | |
| }
 | |
| /** @brief  Interrupt RX serielle Schnittstelle
 | |
|  *  @param  none
 | |
|  *  @return none
 | |
|  */
 | |
| ISR(USART_RX_vect)
 | |
| {
 | |
|     unsigned char data;
 | |
|     data = UDR0;
 | |
|     SerialWriteChar(data);
 | |
| }
 | |
| /** @brief  Initialisierung bei Reset und Neustart
 | |
|  *  @param  none
 | |
|  *  @return none
 | |
|  */
 | |
| void ReStart(void)
 | |
| {
 | |
| 
 | |
|     MenuCtrlTimer = 0;
 | |
|     t_delayms = 0;
 | |
|     bState.SendStatus = SENDING_NOTHING;
 | |
|     
 | |
|     Init();    
 | |
|     DisplayVersion();
 | |
|     ReadEEprom();
 | |
|     WpM = bConfig.WpM;
 | |
|     EncoderPos = bConfig.WpM;
 | |
|     EncoderWrite(bConfig.WpM);
 | |
|     EncoderPosConfig = 1;
 | |
|     SetFrequency(bConfig.SidetoneFreq);
 | |
|     SetRatio(0);
 | |
|     KeyerMode = bConfig.KeyerMode;
 | |
|     PaddleMode = bConfig.Reverse;
 | |
|     bState.KeyTX = 1;
 | |
|     bState.KeyState = 0;
 | |
|     if(bConfig.SidetoneEnabled)
 | |
|         SidetoneEnable();
 | |
|     else
 | |
|         SidetoneDisable();
 | |
| }
 | |
| /** @brief  main()
 | |
|  *  @param  none
 | |
|  *  @return none
 | |
|  */
 | |
| int main(void)
 | |
| {
 | |
|     ReStart();
 | |
|     SerialAbout();
 | |
|     SerialInfo();
 | |
|     while(1)
 | |
|     {
 | |
|         Drehencoder();
 | |
|         // Wenn Geschwindigkeit verändert und Zeit abgelaufen,
 | |
|         // dann im EEprom speichern und Merker löschen.
 | |
|         if(bState.WriteWpMEEprom)
 | |
|         {
 | |
|             sprintf(sdebug," %i WpM in EEprom speichern\r\n", bConfig.WpM);
 | |
|             SerialWriteString(sdebug);
 | |
|             WriteEEpromWpM();
 | |
|             bState.WriteWpMEEprom = 0;
 | |
|         }
 | |
|         if(bState.WriteEEprom)
 | |
|         {
 | |
|             sprintf(sdebug,"Einstellungen in EEprom speichern\r\n");
 | |
|             SerialWriteString(sdebug);
 | |
|             WriteEEprom();
 | |
|             bState.WriteEEprom = 0;
 | |
|         }
 | |
|         UpdateDisplay();
 | |
|         CheckStraightKey();
 | |
|         CheckPaddles();
 | |
|         DitDahBuffers();
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 |