300 lines
8.5 KiB
C++
300 lines
8.5 KiB
C++
/**
|
|
* The user interface of the ubitx consists of the encoder, the push-button on top of it
|
|
* and the 16x2 LCD display.
|
|
* The upper line of the display is constantly used to display frequency and status
|
|
* of the radio. Occasionally, it is used to provide a two-line information that is
|
|
* quickly cleared up.
|
|
*/
|
|
|
|
|
|
/*
|
|
const PROGMEM uint8_t meters_bitmap[] = {
|
|
B10000, B10000, B10000, B10000, B10000, B10000, B10000, B10000 , //custom 1
|
|
B11000, B11000, B11000, B11000, B11000, B11000, B11000, B11000 , //custom 2
|
|
B11100, B11100, B11100, B11100, B11100, B11100, B11100, B11100 , //custom 3
|
|
B11110, B11110, B11110, B11110, B11110, B11110, B11110, B11110 , //custom 4
|
|
B11111, B11111, B11111, B11111, B11111, B11111, B11111, B11111 , //custom 5
|
|
B01000, B11100, B01000, B00000, B10111, B10101, B10101, B10111 //custom 6
|
|
};
|
|
*/
|
|
|
|
//SWR GRAPH, DrawMeter and drawingMeter Logic function by VK2ETA
|
|
|
|
#ifdef OPTION_SKINNYBARS //We want skninny bars with more text
|
|
//VK2ETA modded "Skinny" bitmaps
|
|
const PROGMEM uint8_t meters_bitmap[] = {
|
|
// B01110, B10001, B10001, B11111, B11011, B11011, B11111, B00000, //Padlock Symbol, for merging. Not working, see below
|
|
B00000, B00000, B00000, B00000, B00000, B00000, B00000, B10000, //shortest bar
|
|
B00000, B00000, B00000, B00000, B00000, B00000, B00100, B10100,
|
|
B00000, B00000, B00000, B00000, B00000, B00001, B00101, B10101,
|
|
B00000, B00000, B00000, B00000, B10000, B10000, B10000, B10000,
|
|
B00000, B00000, B00000, B00100, B10100, B10100, B10100, B10100,
|
|
B00000, B00000, B00001, B00101, B10101, B10101, B10101, B10101, //tallest bar
|
|
B00000, B00010, B00111, B00010, B01000, B11100, B01000, B00000, // ++ sign
|
|
};
|
|
#else
|
|
//VK2ETA "Fat" bars, easy to read, with less text
|
|
const PROGMEM uint8_t meters_bitmap[] = {
|
|
// B01110, B10001, B10001, B11111, B11011, B11011, B11111, B00000, //Padlock Symbol, for merging. Not working, see below
|
|
B00000, B00000, B00000, B00000, B00000, B00000, B00000, B11111, //shortest bar
|
|
B00000, B00000, B00000, B00000, B00000, B00000, B11111, B11111,
|
|
B00000, B00000, B00000, B00000, B00000, B11111, B11111, B11111,
|
|
B00000, B00000, B00000, B00000, B11111, B11111, B11111, B11111,
|
|
B00000, B00000, B00000, B11111, B11111, B11111, B11111, B11111,
|
|
B00000, B00000, B11111, B11111, B11111, B11111, B11111, B11111, //tallest bar
|
|
B00000, B00010, B00111, B00010, B01000, B11100, B01000, B00000, // ++ sign
|
|
};
|
|
#endif //OPTION_SKINNYBARS
|
|
PGM_P p_metes_bitmap = reinterpret_cast<PGM_P>(meters_bitmap);
|
|
|
|
const PROGMEM uint8_t lock_bitmap[8] = {
|
|
0b01110,
|
|
0b10001,
|
|
0b10001,
|
|
0b11111,
|
|
0b11011,
|
|
0b11011,
|
|
0b11111,
|
|
0b00000};
|
|
PGM_P plock_bitmap = reinterpret_cast<PGM_P>(lock_bitmap);
|
|
|
|
|
|
// initializes the custom characters
|
|
// we start from char 1 as char 0 terminates the string!
|
|
void initMeter(){
|
|
uint8_t tmpbytes[8];
|
|
byte i;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
tmpbytes[i] = pgm_read_byte(plock_bitmap + i);
|
|
LCD_CreateChar(0, tmpbytes);
|
|
|
|
for (i = 0; i < 8; i++)
|
|
tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i);
|
|
LCD_CreateChar(1, tmpbytes);
|
|
|
|
for (i = 0; i < 8; i++)
|
|
tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i + 8);
|
|
LCD_CreateChar(2, tmpbytes);
|
|
|
|
for (i = 0; i < 8; i++)
|
|
tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i + 16);
|
|
LCD_CreateChar(3, tmpbytes);
|
|
|
|
for (i = 0; i < 8; i++)
|
|
tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i + 24);
|
|
LCD_CreateChar(4, tmpbytes);
|
|
|
|
for (i = 0; i < 8; i++)
|
|
tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i + 32);
|
|
LCD_CreateChar(5, tmpbytes);
|
|
|
|
for (i = 0; i < 8; i++)
|
|
tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i + 40);
|
|
LCD_CreateChar(6, tmpbytes);
|
|
|
|
for (i = 0; i < 8; i++)
|
|
tmpbytes[i] = pgm_read_byte(p_metes_bitmap + i + 48);
|
|
LCD_CreateChar(7, tmpbytes);
|
|
}
|
|
|
|
|
|
//by KD8CEC
|
|
//0 ~ 25 : 30 over : + 10
|
|
/*
|
|
void drawMeter(int needle) {
|
|
//5Char + O over
|
|
int i;
|
|
|
|
for (i = 0; i < 5; i++) {
|
|
if (needle >= 5)
|
|
lcdMeter[i] = 5; //full
|
|
else if (needle > 0)
|
|
lcdMeter[i] = needle; //full
|
|
else //0
|
|
lcdMeter[i] = 0x20;
|
|
|
|
needle -= 5;
|
|
}
|
|
|
|
if (needle > 0)
|
|
lcdMeter[5] = 6;
|
|
else
|
|
lcdMeter[5] = 0x20;
|
|
}
|
|
*/
|
|
//VK2ETA meter for S.Meter, power and SWR
|
|
void drawMeter(int needle)
|
|
{
|
|
#ifdef OPTION_SKINNYBARS
|
|
//Fill buffer with growing set of bars, up to needle value
|
|
lcdMeter[0] = 0x20;
|
|
lcdMeter[1] = 0x20;
|
|
for (int i = 0; i < 6; i++) {
|
|
if (needle > i)
|
|
lcdMeter[i / 3] = byte(i + 1); //Custom characters above
|
|
//else if (i == 1 || i == 4) {
|
|
// lcdMeter[i / 3] = 0x20; //blank
|
|
//}
|
|
}
|
|
|
|
if (needle > 7) {
|
|
lcdMeter[2] = byte(7); //Custom character "++"
|
|
} else if (needle > 6) {
|
|
lcdMeter[2] = '+'; //"+"
|
|
} else lcdMeter[2] = 0x20;
|
|
|
|
|
|
#else //Must be "fat" bars
|
|
//Fill buffer with growing set of bars, up to needle value
|
|
for (int i = 0; i < 6; i++) {
|
|
if (needle > i)
|
|
lcdMeter[i] = byte(i + 1); //Custom characters above
|
|
else
|
|
lcdMeter[i] = 0x20; //blank
|
|
}
|
|
|
|
if (needle > 7) {
|
|
lcdMeter[6] = byte(7); //Custom character "++"
|
|
} else if (needle > 6) {
|
|
lcdMeter[6] = '+'; //"+"
|
|
} else lcdMeter[6] = 0x20;
|
|
|
|
#endif //OPTION_FATBARS
|
|
}
|
|
|
|
|
|
|
|
char byteToChar(byte srcByte){
|
|
if (srcByte < 10)
|
|
return 0x30 + srcByte;
|
|
else
|
|
return 'A' + srcByte - 10;
|
|
}
|
|
|
|
//returns true if the button is pressed
|
|
int btnDown(void){
|
|
#ifdef EXTEND_KEY_GROUP1
|
|
if (analogRead(FBUTTON) > FUNCTION_KEY_ADC)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
|
|
#else
|
|
if (digitalRead(FBUTTON) == HIGH)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
#ifdef EXTEND_KEY_GROUP1
|
|
int getBtnStatus(void){
|
|
int readButtonValue = analogRead(FBUTTON);
|
|
|
|
if (analogRead(FBUTTON) < FUNCTION_KEY_ADC)
|
|
return FKEY_PRESS;
|
|
else
|
|
{
|
|
readButtonValue = readButtonValue / 4;
|
|
//return FKEY_VFOCHANGE;
|
|
for (int i = 0; i < 16; i++)
|
|
if (KeyValues[i][2] != 0 && KeyValues[i][0] <= readButtonValue && KeyValues[i][1] >= readButtonValue)
|
|
return KeyValues[i][2];
|
|
//return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
int enc_prev_state = 3;
|
|
|
|
/**
|
|
* The A7 And A6 are purely analog lines on the Arduino Nano
|
|
* These need to be pulled up externally using two 10 K resistors
|
|
*
|
|
* There are excellent pages on the Internet about how these encoders work
|
|
* and how they should be used. We have elected to use the simplest way
|
|
* to use these encoders without the complexity of interrupts etc to
|
|
* keep it understandable.
|
|
*
|
|
* The enc_state returns a two-bit number such that each bit reflects the current
|
|
* value of each of the two phases of the encoder
|
|
*
|
|
* The enc_read returns the number of net pulses counted over 50 msecs.
|
|
* If the puluses are -ve, they were anti-clockwise, if they are +ve, the
|
|
* were in the clockwise directions. Higher the pulses, greater the speed
|
|
* at which the enccoder was spun
|
|
*/
|
|
|
|
byte enc_state (void) {
|
|
return (analogRead(ENC_A) > 500 ? 1 : 0) + (analogRead(ENC_B) > 500 ? 2: 0);
|
|
}
|
|
|
|
int enc_read(void) {
|
|
int result = 0;
|
|
byte newState;
|
|
int enc_speed = 0;
|
|
|
|
unsigned long start_at = millis();
|
|
|
|
while (millis() - start_at < 50) { // check if the previous state was stable
|
|
newState = enc_state(); // Get current state
|
|
|
|
if (newState != enc_prev_state)
|
|
delay (1);
|
|
|
|
if (enc_state() != newState || newState == enc_prev_state)
|
|
continue;
|
|
//these transitions point to the encoder being rotated anti-clockwise
|
|
if ((enc_prev_state == 0 && newState == 2) ||
|
|
(enc_prev_state == 2 && newState == 3) ||
|
|
(enc_prev_state == 3 && newState == 1) ||
|
|
(enc_prev_state == 1 && newState == 0)){
|
|
result--;
|
|
}
|
|
//these transitions point o the enccoder being rotated clockwise
|
|
if ((enc_prev_state == 0 && newState == 1) ||
|
|
(enc_prev_state == 1 && newState == 3) ||
|
|
(enc_prev_state == 3 && newState == 2) ||
|
|
(enc_prev_state == 2 && newState == 0)){
|
|
result++;
|
|
}
|
|
enc_prev_state = newState; // Record state for next pulse interpretation
|
|
enc_speed++;
|
|
delay(1);
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
//===================================================================
|
|
//I2C Signal Meter, Version 1.097
|
|
//===================================================================
|
|
|
|
// 0xA0 ~ 0xCF : CW Decode Mode + 100Hz ~
|
|
// 0xD0 ~ 0xF3 : RTTY Decode Mode + 100Hz ~
|
|
// 0x10 ~ 0x30 : Spectrum Mode
|
|
int GetI2CSmeterValue(int valueType)
|
|
{
|
|
if (valueType > 0)
|
|
{
|
|
Wire.beginTransmission(I2CMETER_ADDR); //j : S-Meter
|
|
Wire.write(valueType); //Y : Get Value Type
|
|
Wire.endTransmission();
|
|
}
|
|
|
|
Wire.requestFrom(I2CMETER_ADDR, 1);
|
|
|
|
if (Wire.available() > 0)
|
|
{
|
|
return Wire.read();
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|