ubitx-v5d-xcvr/ubitx_20/ubitx_ui.ino
Ashhar Farhan e481ea2a24
The ubitx production sktech, wireup and circuit
This is the snap of the circuit, wiring instructions for the ubitx pcb. the sketch may change slightly for factory alignment but the rest will remain the same.
2017-12-07 10:18:43 +05:30

231 lines
6.1 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.
*/
//returns true if the button is pressed
int btnDown(){
if (digitalRead(FBUTTON) == HIGH)
return 0;
else
return 1;
}
/**
* Meter (not used in this build for anything)
* the meter is drawn using special characters. Each character is composed of 5 x 8 matrix.
* The s_meter array holds the definition of the these characters.
* each line of the array is is one character such that 5 bits of every byte
* makes up one line of pixels of the that character (only 5 bits are used)
* The current reading of the meter is assembled in the string called meter
*/
char meter[17];
byte s_meter_bitmap[] = {
B00000,B00000,B00000,B00000,B00000,B00100,B00100,B11011,
B10000,B10000,B10000,B10000,B10100,B10100,B10100,B11011,
B01000,B01000,B01000,B01000,B01100,B01100,B01100,B11011,
B00100,B00100,B00100,B00100,B00100,B00100,B00100,B11011,
B00010,B00010,B00010,B00010,B00110,B00110,B00110,B11011,
B00001,B00001,B00001,B00001,B00101,B00101,B00101,B11011
};
// initializes the custom characters
// we start from char 1 as char 0 terminates the string!
void initMeter(){
lcd.createChar(1, s_meter_bitmap);
lcd.createChar(2, s_meter_bitmap + 8);
lcd.createChar(3, s_meter_bitmap + 16);
lcd.createChar(4, s_meter_bitmap + 24);
lcd.createChar(5, s_meter_bitmap + 32);
lcd.createChar(6, s_meter_bitmap + 40);
}
/**
* The meter is drawn with special characters.
* character 1 is used to simple draw the blocks of the scale of the meter
* characters 2 to 6 are used to draw the needle in positions 1 to within the block
* This displays a meter from 0 to 100, -1 displays nothing
*/
void drawMeter(int8_t needle){
int16_t best, i, s;
if (needle < 0)
return;
s = (needle * 4)/10;
for (i = 0; i < 8; i++){
if (s >= 5)
meter[i] = 1;
else if (s >= 0)
meter[i] = 2 + s;
else
meter[i] = 1;
s = s - 5;
}
if (needle >= 40)
meter[i-1] = 6;
meter[i] = 0;
}
// The generic routine to display one line on the LCD
void printLine(char linenmbr, char *c) {
if (strcmp(c, printBuff[linenmbr])) { // only refresh the display when there was a change
lcd.setCursor(0, linenmbr); // place the cursor at the beginning of the selected line
lcd.print(c);
strcpy(printBuff[linenmbr], c);
for (byte i = strlen(c); i < 16; i++) { // add white spaces until the end of the 16 characters line is reached
lcd.print(' ');
}
}
}
// short cut to print to the first line
void printLine1(char *c){
printLine(1,c);
}
// short cut to print to the first line
void printLine2(char *c){
printLine(0,c);
}
// this builds up the top line of the display with frequency and mode
void updateDisplay() {
// tks Jack Purdum W8TEE
// replaced fsprint commmands by str commands for code size reduction
memset(c, 0, sizeof(c));
memset(b, 0, sizeof(b));
ultoa(frequency, b, DEC);
if (inTx){
if (cwTimeout > 0)
strcpy(c, " CW:");
else
strcpy(c, " TX:");
}
else {
if (ritOn)
strcpy(c, "RIT ");
else {
if (isUSB)
strcpy(c, "USB ");
else
strcpy(c, "LSB ");
}
if (vfoActive == VFO_A) // VFO A is active
strcat(c, "A:");
else
strcat(c, "B:");
}
//one mhz digit if less than 10 M, two digits if more
if (frequency < 10000000l){
c[6] = ' ';
c[7] = b[0];
strcat(c, ".");
strncat(c, &b[1], 3);
strcat(c, ".");
strncat(c, &b[4], 3);
}
else {
strncat(c, b, 2);
strcat(c, ".");
strncat(c, &b[2], 3);
strcat(c, ".");
strncat(c, &b[5], 3);
}
if (inTx)
strcat(c, " TX");
printLine(1, c);
/*
//now, the second line
memset(c, 0, sizeof(c));
memset(b, 0, sizeof(b));
if (inTx)
strcat(c, "TX ");
else if (ritOn)
strcpy(c, "RIT");
strcpy(c, " \xff");
drawMeter(meter_reading);
strcat(c, meter);
strcat(c, "\xff");
printLine2(c);*/
}
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;
long stop_by = millis() + 50;
while (millis() < stop_by) { // 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);
}