You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

383 lines
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();
}
}