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.

721 lines
18 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
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 "bj-keyer.h"
// Additional files
#include "functions.c"
#define LENGTH 256
#define AMP 127
#define OFFSET 128
#define PI2 6.283185
/**
* \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);
// 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 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); // Enable Timer 0 CTC
sei();
}
void Init()
{
cli(); // disable all interrupts
MachineMode = NORMAL;
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 abschalten
// cbi(PORTB,AUDIO_EN);
// 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);
t_element_length = (uint16_t)1200/bConfig.wpm;
// Pin Change Interrupts Port D - Keys
// 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.WriteEEProm = 0;
bMenuCtrl.buttonPressedLong = 0;
// Initialisierung Konfiguration
bConfig.iambic = 1;
bConfig.sidetone_f = 600;
bConfig.sidetone = 1;
bConfig.trx = 0;
bConfig.weight = 50;
bConfig.wpmbpm = 1;
bConfig.wpm = 15;
bConfig.ratio = 30;
bConfig.reverse = 0;
sei(); // enable all interrupts
}
/** \brief 16 Bit Timer 1A
*
* Timer 1A interrupt
* Overflow interrupt every 64µs
*
*/
//ISR(TIMER1_OVF_vect)
//{
// //sCurrentTimer += 0xffff;
// PORTD ^= (1<<PD1);
//}
//
ISR(TIMER1_COMPA_vect)
{
OCR2A = pgm_read_byte_near(sinewave+icnt);
icnt++;
if(icnt > SINEWAVELENGTH - 1)
icnt = 0;
}
/** \brief 8 Bit Timer 0
*
* The Timer 0 CTC interrupt
* Dieser Interrupt wird jede Millisekunde erzeugt
*
*/
ISR(TIMER0_COMPA_vect)
{
ms++;
StoreEEprom++;
MenuCtrlTimer++;
mselement++; // element length of dit or dat
t_wait++;
l_timer++;
encoder_timer++;
if(l_timer >= L_WAIT){
l_timer = 0;
}
// Alle 5ms den Drehencoder abfragen
if(encoder_timer > 5) {
EncoderPolling();
// Schalter vom Drehencoder abfragen
if(EncoderGetButtonState() == ButtonPressed_Short)
{
bMenuCtrl.buttonPressed = 1;
SendSerialString("Button pressed short\r\n");
}
if(EncoderGetButtonState() == ButtonPressed_Long)
{
bMenuCtrl.buttonPressedLong = 1;
SendSerialString("Button pressed long\r\n");
}
}
// Wpm verändert?
if((StoreEEprom > 1000) && (bMerker.WpMChanged))
{
StoreEEprom = 0;
bMerker.WriteWpMEEProm = 1;
bMerker.WpMChanged = 0;
}
// Konfiguration nach 3 Sekunden verlassen
// if((MenuCtrlTimer > 3000) && (bMenuCtrl.Config == 1))
// {
// bMenuCtrl.Config = 0;
// bMenuCtrl.Update = 1;
// bMenuCtrl.ClrScr = 1;
// BeepBoop();
// }
}
/** \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)
{
// phaccu = phaccu + tword_m;
// icnt = phaccu >> 24;
// OCR2A = pgm_read_byte_near(sinewave+icnt);
}
/** \brief Pin Change Interupts für Paddle und StraightKey
*
* Pin Change Interrupt Vector für die Tasteneingänge
* Str Pa2 Pa1
* PORTD PD7 PD6 PD5 PD4 PD3 PD2 PD1 PD0
* x80 x40 x20 x10 x8 x4 x2 x1
*
*/
ISR(PCINT2_vect)
{
uint8_t changedbits;
changedbits = PIND ^ keyhistory;
keyhistory = PIND;
if(changedbits & (1<<STRAIGHT_KEY))
{
if(PIND & (1<<STRAIGHT_KEY))
{
PORTC &= ~(1<<MORSE_LED);
SideToneOff();
}
else
{
PORTC |= (1<<MORSE_LED);
SideToneOn();
}
}
}
ISR(USART_RX_vect)
{
unsigned char data;
data = UDR0;
SendSerialChar(data);
}
/** \brief ClearSendBuffer
*
*/
void ClearSendBuffer(void)
{
// sendbufferbytes = 0;
}
/** \brief CheckDitPaddle
*
*
*/
void CheckDitPaddle(void)
{
uint8_t pinvalue = 0;
uint8_t ditpaddle = 0;
if(PaddleMode == PADDLE_NORMAL) // no reverse paddle
ditpaddle = LEFT_PADDLE;
else
ditpaddle = RIGHT_PADDLE; // reverse paddle
pinvalue = ReadKeyPin(ditpaddle);
if(pinvalue == 0)
DitBuffer = 1;
}
/*
** CheckDahPaddle
*/
void CheckDahPaddle(void)
{
uint8_t pinvalue = 0;
uint8_t dahpaddle = 0;
if(PaddleMode == PADDLE_NORMAL) // no reverse paddle
dahpaddle = RIGHT_PADDLE;
else
dahpaddle = LEFT_PADDLE; // reverse paddle
pinvalue = ReadKeyPin(dahpaddle);
if(pinvalue == 0) {
if(DahBuffer == 0) {
DahCounter++;
DitCounter = 0;
}
DahBuffer = 1;
}
}
/*
** DoMorse
*/
void DoMorse(void)
{
if((KeyerMode == IAMBIC_A) || (KeyerMode == IAMBIC_B) || KeyerMode == SINGLE_PADDLE)
{
if((KeyerMode == IAMBIC_A) && (IambicFlag) && (ReadKeyPin(LEFT_PADDLE)))
{
IambicFlag = 0;
DitBuffer = 0;
DahBuffer = 0;
}
else
{
if(DitBuffer)
{
DitBuffer = 0;
SendDit(MANUAL_SENDING);
}
if(DahBuffer)
{
DahBuffer = 0;
SendDah(MANUAL_SENDING);
}
}
}
else
{
if(KeyerMode == STRAIGHT)
{
if(DitBuffer)
{
DitBuffer = 0;
TXSidetoneKey(1,MANUAL_SENDING);
}
else
{
TXSidetoneKey(0,MANUAL_SENDING);
}
DitCounter = 0;
}
}
}
void ConfigMenue(void)
{
char line[22];
lcd_charMode(NORMAL);
lcd_gotoxy(0,0);
sprintf(line,"%s - %i","Konfiguration", bMenuCtrl.CurMenue);
lcd_puts(line);
lcd_charMode(DOUBLESIZE);
lcd_gotoxy(0,3);
lcd_puts(CLEARLINE);
switch(bMenuCtrl.CurMenue)
{
case M_TRX1:
lcd_gotoxy(0,3);
if((bConfig.trx == 1) || (bConfig.trx == 0))
sprintf(line,"[%s]", "TRX 1");
else
sprintf(line,"%s", "TRX 1");
lcd_puts(line);
break;
case M_TRX2:
lcd_gotoxy(0,3);
if((bConfig.trx == 2) || (bConfig.trx == 0))
sprintf(line,"[%s]", "TRX 2");
else
sprintf(line,"%s", "TRX 2");
lcd_puts(line);
break;
case M_IAMBICA:
lcd_gotoxy(0,3);
if(bConfig.iambic == 1)
sprintf(line,"[%s]", "Iambic A");
else
sprintf(line,"%s", "Iambic A");
lcd_puts(line);
break;
case M_IAMBICB:
lcd_gotoxy(0,3);
if(bConfig.iambic == 2)
sprintf(line,"[%s]", "Iambic B");
else
sprintf(line,"%s", "Iambic B");
lcd_puts(line);
break;
case M_REVERSE:
lcd_gotoxy(0,3);
if(bConfig.reverse == 0)
sprintf(line,"%s", "L . R -");
else
sprintf(line,"%s", "L - R .");
lcd_puts(line);
break;
case M_RATIO:
lcd_gotoxy(0,3);
if(bConfig.ratio == 30)
sprintf(line,"%s", "Ratio 3:1");
else
sprintf(line,"%s %f:1", "Ratio", bConfig.ratio/10);
lcd_puts(line);
break;
case M_TON_FREQ:
lcd_gotoxy(0,3);
if(bConfig.sidetone_f == 650)
sprintf(line,"%s", "Ton 650Hz");
else
sprintf(line,"%s %uHz", "Ton", bConfig.sidetone_f);
lcd_puts(line);
break;
case M_TON:
lcd_gotoxy(0,3);
if(bConfig.ratio == 1)
sprintf(line,"%s", "Ton an");
else
sprintf(line,"%s", "Ton aus");
lcd_puts(line);
break;
case M_WPMBPM:
lcd_gotoxy(0,3);
if(bConfig.wpmbpm == 0)
sprintf(line,"%s", "WpM");
else
sprintf(line,"%s", "BpM");
lcd_puts(line);
break;
}
bMenuCtrl.Update = 0;
lcd_charMode(NORMAL);
}
/** \brief UpdateDisplay
*
* Aktualisierung der Anzeigen auf dem Display je nach
* aktueller Funktion.
*
* DOUBLESIZE 4x10 character
* NORMALSIZE 8x21 character
*/
void UpdateDisplay(void)
{
char line[22];
if(bMenuCtrl.Update)
{
if(bMenuCtrl.ClrScr)
{
lcd_clrscr();
bMenuCtrl.ClrScr = 0;
}
if(!(bMenuCtrl.Config))
{
lcd_charMode(DOUBLESIZE);
lcd_gotoxy(4,3);
if(bConfig.wpmbpm)
sprintf(line,"%i WpM ",bConfig.wpm);
else
sprintf(line,"%i BpM ", bConfig.wpm*5);
lcd_puts(line);
lcd_charMode(NORMAL);
lcd_gotoxy(13,0);
if(bConfig.iambic == 1)
sprintf(line,"%s", IambicA);
if(bConfig.iambic == 2)
sprintf(line,"%s", IambicB);
lcd_puts(line);
lcd_gotoxy(0,0);
if(bConfig.trx == 1)
sprintf(line, "%s", Trx1);
if(bConfig.trx == 2)
sprintf(line, "%s", Trx2);
if(bConfig.trx == 0)
sprintf(line, "%s %s", Trx1, Trx2);
lcd_puts(line);
}
if(bMenuCtrl.Config)
{
ConfigMenue();
}
bMenuCtrl.Update = 0;
}
}
void Drehencoder(void)
{
int8_t st = 0;
static int8_t last;
if(!(bMenuCtrl.Config))
{
EncoderMinMax(5,50);
st = EncoderRead(1);
if(bConfig.wpm != st)
{
bConfig.wpm = st;
bMerker.WpMChanged = 1;
bMenuCtrl.Update = 1;
}
}
if((bMenuCtrl.buttonPressed == 1) && (bMenuCtrl.Config == 0))
{
bMenuCtrl.Config = 1;
MenuCtrlTimer = 0;
bMenuCtrl.buttonPressed = 0;
}
if((bMenuCtrl.buttonPressedLong == 1) && (bMenuCtrl.Config == 1))
{
bMenuCtrl.Config = 0;
bMenuCtrl.Update = 1;
bMenuCtrl.buttonPressedLong = 0;
bMenuCtrl.buttonPressed = 0;
bMenuCtrl.m_buttonPressed = 0;
bMenuCtrl.m_buttonPressed = 0;
MenuCtrlTimer = 0;
}
if(bMenuCtrl.Config == 1)
{
if(!bMenuCtrl.buttonPressed)
{
EncoderMinMax(1,M_MAX);
st = EncoderRead(1);
sprintf(sdebug,"Encoder %i\r\n",st);
SendSerialString(sdebug);
if(last != st)
{
bMenuCtrl.CurMenue = st;
bMenuCtrl.Update = 1;
}
last = st;
}
if(bMenuCtrl.buttonPressed)
{
bMenuCtrl.m_buttonPressed = 1;
bMenuCtrl.buttonPressed = 0;
}
if(bMenuCtrl.m_buttonPressed == 1)
{
UpdateDisplay();
switch(bMenuCtrl.CurMenue)
{
case M_TRX1:
if(bConfig.trx == 2)
bConfig.trx = 0;
else
bConfig.trx = 1;
bMenuCtrl.m_buttonPressed = 0;
break;
case M_TRX2:
if(bConfig.trx == 1)
bConfig.trx = 0;
else
bConfig.trx = 2;
bMenuCtrl.m_buttonPressed = 0;
break;
case M_IAMBICA:
bConfig.iambic = 1;
bMenuCtrl.m_buttonPressed = 0;
break;
case M_IAMBICB:
bConfig.iambic = 2;
bMenuCtrl.m_buttonPressed = 0;
break;
case M_REVERSE:
if(bConfig.reverse == 1)
bConfig.reverse = 0;
else
bConfig.reverse = 1;
bMenuCtrl.m_buttonPressed = 0;
break;
case M_RATIO:
EncoderMinMax(15,30);
st = EncoderRead(1);
bConfig.ratio = st;
if(bConfig.ratio > 30) bConfig.ratio = 30;
if(bConfig.ratio < 15) bConfig.ratio = 15;
bMenuCtrl.Update = 1;
break;
case M_TON_FREQ:
break;
}
}
}
}
/*
** main
*/
int main(void)
{
Init();
SideToneOff();
lcd_init(LCD_DISP_ON);
lcd_charMode(DOUBLESIZE);
lcd_home();
lcd_puts(PRG);
lcd_gotoxy(1,2);
lcd_puts(VER);
lcd_gotoxy(2,4);
lcd_puts(CALL);
delayms(1000);
MachineMode = NORMAL;
KeyerMode = IAMBIC_A;
ReadEEProm_WpM();
SendSerialString(CLRSCR);
SendSerialString("BJ-Keyer V1.00\r\n");
SendSerialString("Ready!\r\n");
EncoderWrite(bConfig.wpm);
BeepBoop();
SetFrequency(600);
while(1)
{
Drehencoder();
if(bMerker.WriteWpMEEProm)
WriteEEProm_WpM();
UpdateDisplay();
// if(MachineMode == NORMAL)
// {
// CheckPaddles();
// DoMorse();
// }
if(MachineMode == COMMAND)
{
}
}
}