1 | #include <stdio.h>
|
2 | #include <stdlib.h>
|
3 | #include <avr/io.h>
|
4 | #include <avr/interrupt.h>
|
5 | #include <util/delay.h>
|
6 | #include <avr/pgmspace.h>
|
7 |
|
8 | #include "define.h"
|
9 | #include "RX5808.h"
|
10 | #include "max7456.h"
|
11 | #include "uart.h"
|
12 |
|
13 | //******************************************************************************
|
14 | //* declar
|
15 | //******************************************************************************
|
16 |
|
17 | RX5808 rx;
|
18 | Max7456 osd;
|
19 | uint8_t currentChannel = 0;
|
20 | char buffer[5]; //Output of the itoa function
|
21 |
|
22 | //forward declaration
|
23 | uint8_t getClickType();
|
24 |
|
25 | //******************************************************************************
|
26 | //* AVR IO Funktion
|
27 | //******************************************************************************
|
28 | int freeRam () {
|
29 | extern int __heap_start, *__brkval;
|
30 | int v;
|
31 | return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
|
32 | }
|
33 | uint16_t read_adc(uint8_t channel){
|
34 | ADMUX &= 0xF0; //Clear the older channel that was read
|
35 | ADMUX |= channel; //Defines the new ADC channel to be read
|
36 | ADCSRA |= (1<<ADSC); //Starts a new conversion
|
37 | while(ADCSRA & (1<<ADSC)); //Wait until the conversion is done
|
38 | return ADCW; //Returns the ADC value of the chosen channel
|
39 | }
|
40 |
|
41 | void printFreeMEM(){
|
42 | uart_puts_P("free: ");
|
43 | itoa(freeRam(), buffer, 10); //Convert the read value to an ascii string
|
44 | // itoa(static_cast<uint8_t>(get_mem_unused()), buffer, 10); //Convert the read value to an ascii string
|
45 | uart_puts(buffer);
|
46 | uart_puts_P("\n");
|
47 | }
|
48 |
|
49 | //******************************************************************************
|
50 | //* init
|
51 | //******************************************************************************
|
52 |
|
53 | void init(){
|
54 | //---------------------------------
|
55 | // initialize LED pin as output
|
56 | //---------------------------------
|
57 | DDRB |= (1<<LED_PIN);
|
58 | _delay_ms(20);
|
59 |
|
60 | //---------------------------------
|
61 | // initialize button pin as input
|
62 | //---------------------------------
|
63 | DDRD &= ~(1 << BUTTON_PIN);
|
64 | PORTD |= (1<<LED_PIN); //set internall pullup
|
65 |
|
66 | //---------------------------------
|
67 | // initialize analog input
|
68 | //---------------------------------
|
69 | ADCSRA |= ((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)); //16Mhz/128 = 125Khz the ADC reference clock
|
70 | ADMUX |= (1<<REFS0); //Voltage reference from Avcc (5v)
|
71 | ADCSRA |= (1<<ADEN); //Turn on ADC
|
72 | ADCSRA |= (1<<ADSC); //Do an initial conversion because this one is the slowest and to ensure that everything is up and running
|
73 |
|
74 | //---------------------------------
|
75 | // initialize timer 0
|
76 | //---------------------------------
|
77 | //(8000000/8)/256 Hz = 390.000 Hz -> 2,56 ms
|
78 | //TCCR0 = (1<<CS01); // Prescaler 8
|
79 | //TIMSK |= (1<<TOIE0); // Overflow Interrupt
|
80 | sei(); // interrupt allow
|
81 |
|
82 | //---------------------------------
|
83 | // initialize uart
|
84 | //---------------------------------
|
85 | uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
|
86 | sei();
|
87 | //---------------------------------
|
88 | // OSD initialize Settings
|
89 | //---------------------------------
|
90 | osd.setDisplayOffsets(45,25); //x , y
|
91 | osd.setBlinkParams( _8fields , _3BT_BT );
|
92 | osd.activateExternalVideo();
|
93 |
|
94 | }
|
95 | //******************************************************************************
|
96 | // RX5808
|
97 | //******************************************************************************
|
98 |
|
99 | //******************************************************************************
|
100 | //* Positions in the frequency table for the 40 channels
|
101 | //* Direct access via array operations does not work since data is stored in
|
102 | //* flash, not in RAM. Use getPosition to retrieve data
|
103 |
|
104 | const uint8_t positions[] PROGMEM = {
|
105 | 19, 18, 32, 17, 33, 16, 7, 34, 8, 24, 6, 9, 25, 5, 35, 10, 26, 4, 11, 27,
|
106 | 3, 36, 12, 28, 2, 13, 29, 37, 1, 14, 30, 0, 15, 31, 38, 20, 21, 39, 22, 23
|
107 | };
|
108 |
|
109 | uint16_t getPosition( uint8_t channel ) {
|
110 | return pgm_read_byte_near(positions + channel);
|
111 | }
|
112 |
|
113 | //******************************************************************************
|
114 | //* Frequencies for the 40 channels
|
115 | //* Direct access via array operations does not work since data is stored in
|
116 | //* flash, not in RAM. Use getFrequency to retrieve data
|
117 |
|
118 | const uint16_t channelFrequencies[] PROGMEM = {
|
119 | 5865, 5845, 5825, 5805, 5785, 5765, 5745, 5725, // Band A - Boscam A
|
120 | 5733, 5752, 5771, 5790, 5809, 5828, 5847, 5866, // Band B - Boscam B
|
121 | 5705, 5685, 5665, 5645, 5885, 5905, 5925, 5945, // Band E - DJI
|
122 | 5740, 5760, 5780, 5800, 5820, 5840, 5860, 5880, // Band F - FatShark \ Immersion
|
123 | 5658, 5695, 5732, 5769, 5806, 5843, 5880, 5917 // Band C - Raceband
|
124 | };
|
125 |
|
126 | uint16_t getFrequency( uint8_t channel ) {
|
127 | return pgm_read_word_near(channelFrequencies + getPosition(channel));
|
128 | }
|
129 |
|
130 | //******************************************************************************
|
131 | //* function: nextChannel
|
132 | //******************************************************************************
|
133 | uint8_t nextChannel(uint8_t channel)
|
134 | {
|
135 | if (channel > (CHANNEL_MAX - 1))
|
136 | return CHANNEL_MIN;
|
137 | else
|
138 | return channel + 1;
|
139 |
|
140 | }//******************************************************************************
|
141 | //* function: updateScannerScreen
|
142 | //* : position = 0 to 99
|
143 | //* : value = 0 to 53
|
144 | //* : must be fast since there are frequent updates
|
145 | //******************************************************************************
|
146 | void updateScannerScreen(uint8_t position, uint8_t value ) {
|
147 |
|
148 | osd.print( read_adc(RSSI_PIN), 5, 5, 4, 0);
|
149 |
|
150 | }
|
151 |
|
152 | //******************************************************************************
|
153 | //* function: drawAutoScanScreen
|
154 | //******************************************************************************
|
155 | void drawScanScreen( uint16_t Mhz, uint8_t channel , uint16_t rssi ) {
|
156 |
|
157 | osd.print("Auto Scanner", 7, 3);
|
158 | osd.print("Channel", 2, 6);
|
159 | osd.print(" MHz", 12, 6);
|
160 | osd.print("RSSI ", 20, 6);
|
161 | osd.print( channel, 2, 8, 2, 0);
|
162 | osd.print( Mhz, 12, 8, 4, 0);
|
163 | osd.print( rssi, 20, 8, 4, 0);
|
164 |
|
165 | }
|
166 | //******************************************************************************
|
167 | //* function: drawScannerScreen
|
168 | //******************************************************************************
|
169 |
|
170 | void drawScannerScreen( void ) {
|
171 |
|
172 | osd.printMax7456Chars( bar, 25, 2, 12);
|
173 | osd.print("5.65 5.8 5.95", 2, 13);
|
174 | updateScannerScreen(0, 0);
|
175 | }
|
176 |
|
177 | //******************************************************************************
|
178 | //* function: autoScan
|
179 | //******************************************************************************
|
180 | uint16_t autoScan( uint16_t frequency ) {
|
181 |
|
182 | uint8_t i;
|
183 | uint16_t scanRssi = 0;
|
184 | uint16_t bestRssi = 0;
|
185 | uint16_t scanFrequency;
|
186 | uint16_t bestFrequency;
|
187 |
|
188 | drawScanScreen(0,0,0);
|
189 |
|
190 | // Skip 10 MHz forward to avoid detecting the current channel
|
191 | scanFrequency = frequency + 10;
|
192 | if (!(scanFrequency % 2))
|
193 | scanFrequency++; // RTC6715 can only generate odd frequencies
|
194 |
|
195 | // Coarse tuning
|
196 | bestFrequency = scanFrequency;
|
197 | for (i = 0; i < 60 && (scanRssi < RSSI_TRESHOLD); i++) {
|
198 | if ( scanFrequency <= (FREQUENCY_MAX - 5))
|
199 | scanFrequency += 5;
|
200 | else
|
201 | scanFrequency = FREQUENCY_MIN;
|
202 | rx.setFrequency(scanFrequency);
|
203 | _delay_ms( RSSI_STABILITY_DELAY_MS );
|
204 | scanRssi = read_adc(RSSI_PIN);
|
205 | if (bestRssi < scanRssi) {
|
206 | bestRssi = scanRssi;
|
207 | bestFrequency = scanFrequency;
|
208 | }
|
209 | #ifdef debugSerialScanner
|
210 | char buffer[5]; //Output of the itoa function
|
211 | USART_putstring("F: ");
|
212 | itoa(bestFrequency, buffer, 10); //Convert the read value to an ascii string
|
213 | USART_putstring(buffer);
|
214 | USART_putstring("RSSI: ");
|
215 | itoa(bestRssi, buffer, 10); //Convert the read value to an ascii string
|
216 | USART_putstring(buffer); //Send the converted value to the terminal
|
217 | USART_putstring("\r\n");
|
218 |
|
219 | #endif
|
220 | }
|
221 | // Fine tuning
|
222 | scanFrequency = bestFrequency - 20;
|
223 | bestRssi = 0;
|
224 | for (i = 0; i < 20; i++, scanFrequency += 2) {
|
225 | rx.setFrequency(scanFrequency);
|
226 | _delay_ms( RSSI_STABILITY_DELAY_MS );
|
227 | scanRssi = read_adc(RSSI_PIN);
|
228 | if (bestRssi < scanRssi) {
|
229 | bestRssi = scanRssi;
|
230 | bestFrequency = scanFrequency;
|
231 | }
|
232 | #ifdef debugSerialScanner
|
233 | USART_putstring("F: ");
|
234 | itoa(bestFrequency, buffer, 10); //Convert the read value to an ascii string
|
235 | USART_putstring(buffer);
|
236 | USART_putstring("RSSI: ");
|
237 | itoa(bestRssi, buffer, 10); //Convert the read value to an ascii string
|
238 | USART_putstring(buffer); //Send the converted value to the terminal
|
239 | USART_putstring("\r\n");
|
240 | #endif
|
241 | }
|
242 |
|
243 | drawScanScreen(bestFrequency,0, bestRssi);
|
244 |
|
245 | while (!getClickType()){
|
246 | #ifdef debugSerialScanner
|
247 | USART_putstring("wait");
|
248 | #endif
|
249 | }
|
250 | // Return the best frequency
|
251 | rx.setFrequency(bestFrequency);
|
252 | return (bestFrequency);
|
253 |
|
254 | }
|
255 |
|
256 | //******************************************************************************
|
257 | //* function: graphicScanner
|
258 | //* : scans the 5.8 GHz band in 3 MHz increments and draws a graphical
|
259 | //* : representation. when the button is pressed the currently
|
260 | //* : scanned frequency is returned.
|
261 | //******************************************************************************
|
262 | uint16_t graphicScanner( uint16_t frequency ) {
|
263 | uint8_t i;
|
264 | uint16_t scanRssi;
|
265 | uint16_t bestRssi = 0;
|
266 | uint16_t scanFrequency = frequency;
|
267 | uint16_t bestFrequency = frequency;
|
268 | uint8_t rssiDisplayValue;
|
269 |
|
270 | // Draw screen frame etc
|
271 | drawScannerScreen();
|
272 |
|
273 | while ( getClickType() == NO_CLICK) {
|
274 | scanFrequency += 3;
|
275 | if (scanFrequency > FREQUENCY_MAX)
|
276 | scanFrequency = FREQUENCY_MIN;
|
277 | rx.setFrequency(scanFrequency);
|
278 | _delay_ms( RSSI_STABILITY_DELAY_MS );
|
279 | scanRssi = read_adc(1);
|
280 | rssiDisplayValue = (scanRssi - 140) / 10; // Roughly 2 - 46
|
281 | updateScannerScreen(100 - ((FREQUENCY_MAX - scanFrequency) / 3), rssiDisplayValue );
|
282 |
|
283 | #ifdef debugSerialScanner
|
284 | USART_putstring(": ");
|
285 | itoa(bestFrequency, buffer, 10); //Convert the read value to an ascii string
|
286 | USART_putstring(buffer);
|
287 | USART_putstring("RSSI: ");
|
288 | itoa(bestRssi, buffer, 10); //Convert the read value to an ascii string
|
289 | USART_putstring(buffer); //Send the converted value to the terminal
|
290 | USART_putstring("\r\n");
|
291 | #endif
|
292 | }
|
293 | // Fine tuning
|
294 | scanFrequency = scanFrequency - 20;
|
295 | for (i = 0; i < 20; i++, scanFrequency += 2) {
|
296 | rx.setFrequency(scanFrequency);
|
297 | _delay_ms( RSSI_STABILITY_DELAY_MS );
|
298 | scanRssi = read_adc(RSSI_PIN);
|
299 | if (bestRssi < scanRssi) {
|
300 | bestRssi = scanRssi;
|
301 | bestFrequency = scanFrequency;
|
302 | }
|
303 | }
|
304 | while (!getClickType())
|
305 | // Return the best frequency
|
306 | rx.setFrequency(bestFrequency);
|
307 | return (bestFrequency);
|
308 | }
|
309 | //******************************************************************************
|
310 | //* function: bestChannelMatch
|
311 | //* : finds the best matching standard channel for a given frequency
|
312 | //******************************************************************************
|
313 | uint8_t bestChannelMatch( uint16_t frequency )
|
314 | {
|
315 | int16_t comp;
|
316 | int16_t bestComp = 300;
|
317 | uint8_t bestChannel = CHANNEL_MIN;
|
318 | uint8_t i;
|
319 |
|
320 | for (i = CHANNEL_MIN; i <= CHANNEL_MAX; i++) {
|
321 | comp = abs( (int16_t)getFrequency(i) - (int16_t)frequency );
|
322 | if (comp < bestComp)
|
323 | {
|
324 | bestComp = comp;
|
325 | bestChannel = i;
|
326 | }
|
327 | }
|
328 | return bestChannel;
|
329 | }
|
330 |
|
331 | //******************************************************************************
|
332 | // Menu
|
333 | //******************************************************************************
|
334 |
|
335 | //******************************************************************************
|
336 | //* function: get_click_type
|
337 | //* : Polls the specified pin and returns the type of click that was
|
338 | //* : performed NO_CLICK, SINGLE_CLICK, DOUBLE_CLICK, LONG_CLICK
|
339 | //* : or LONG_LONG_CLICK
|
340 | //******************************************************************************
|
341 |
|
342 | uint8_t getClickType() {
|
343 | uint16_t timer = 0;
|
344 | uint8_t click_type = NO_CLICK;
|
345 |
|
346 | // check if the key has been pressed
|
347 | if (!BUTTON_PRESSED) return NO_CLICK ;
|
348 |
|
349 | while (BUTTON_PRESSED) {
|
350 | timer++;
|
351 | _delay_ms(1);
|
352 | }
|
353 | if (timer < 600)
|
354 | click_type = SINGLE_CLICK;
|
355 |
|
356 | if (timer >= 600)
|
357 | click_type = LONG_CLICK;
|
358 |
|
359 | // Check if there is a second click
|
360 | if ((click_type == SINGLE_CLICK) ) {
|
361 |
|
362 | }
|
363 |
|
364 | while ((!BUTTON_PRESSED) && (timer++ < 120)) {
|
365 | _delay_ms(1);
|
366 | }
|
367 | if (timer >= 200) {
|
368 | return click_type;
|
369 | }
|
370 | if (BUTTON_PRESSED ) {
|
371 | click_type = DOUBLE_CLICK;
|
372 | while (BUTTON_PRESSED) ;
|
373 | }
|
374 | return (click_type);
|
375 | }
|
376 |
|
377 | //******************************************************************************
|
378 | //Flags
|
379 | struct Flags {
|
380 | bool MENUEND;
|
381 | bool ACTIVEITEM;
|
382 | bool REFRESHINTERVAL;
|
383 | };
|
384 |
|
385 | struct MenuEntry ; // dummy
|
386 |
|
387 | typedef void (*MenuFnct) (struct MenuEntry*);
|
388 | extern struct MenuEntry MainMenu[];
|
389 |
|
390 | struct MenuEntry
|
391 | {
|
392 | char* text;
|
393 | MenuFnct function;
|
394 | Flags flags;
|
395 | // struct MenuEntry *subMenu;
|
396 | // uint8_t storage;
|
397 | };
|
398 |
|
399 | void MenuRefresh(struct MenuEntry *Menu );
|
400 | MenuEntry *ptrMenu = NULL;
|
401 | MenuEntry *ptrOldMenu = NULL;
|
402 |
|
403 | void drawMenu(struct MenuEntry *Menu){
|
404 | ptrMenu = Menu;
|
405 | MenuRefresh(ptrMenu);
|
406 | }
|
407 |
|
408 | void exit(struct MenuEntry *Menu){
|
409 | osd.clearScreen();
|
410 | }
|
411 |
|
412 | void AutoSearch (struct MenuEntry *Menu)
|
413 | {
|
414 | osd.clearScreen();
|
415 | currentChannel = bestChannelMatch(autoScan(getFrequency(currentChannel)));
|
416 | ptrMenu = MainMenu;
|
417 | }
|
418 |
|
419 | void BandScanner (struct MenuEntry *Menu)
|
420 | {
|
421 | osd.clearScreen();
|
422 | currentChannel = bestChannelMatch(graphicScanner(getFrequency(currentChannel)));
|
423 | ptrMenu = MainMenu;
|
424 |
|
425 | }
|
426 |
|
427 | void ManualMode (struct MenuEntry *Menu)
|
428 | {
|
429 | uart_puts_p("ManualMode");
|
430 | while (!(getClickType() == LONG_CLICK)) {
|
431 |
|
432 | currentChannel = nextChannel( currentChannel );
|
433 | rx.setFrequency(getFrequency(currentChannel));
|
434 | }
|
435 | }
|
436 | /*
|
437 | struct MenuEntry Settings[] =
|
438 | {
|
439 | { "Batterie BEEPS", NULL , { 0 , 0 , 0 } , NULL , 0 },
|
440 | { "Calibrate RSSI", NULL , { 0 , 0 , 0 } , NULL , 0 },
|
441 | { "Video Input", NULL , { 0 , 0 , 0 } , NULL , 0 },
|
442 | { "Back", drawMenu , { 1 , 0 , 0} , MainMenu , 0 },
|
443 | };
|
444 | */
|
445 | struct MenuEntry MainMenu[] =
|
446 | {
|
447 | { "Auto Scanner", AutoSearch, { 0 , 1 , 0 } /*, NULL , 0*/},
|
448 | { "Band Scanner", BandScanner, { 0 , 0 , 0 } /*, NULL , 0 */},
|
449 | { "Manual Mode", ManualMode, { 0 , 0 , 0 } /*, NULL , 0 */},
|
450 | { "Settings", drawMenu , { 0 , 0 , 0 } /*, NULL , 0 */},
|
451 | { "Exit", exit , { 1 , 0 , 0} /*, NULL , 0 */},
|
452 |
|
453 | };
|
454 |
|
455 | void MenuRefresh(struct MenuEntry *Menu )
|
456 | { //*----------------frame---------------
|
457 | osd.activateOSD();
|
458 | osd.clearScreen();
|
459 | osd.print("CYCLOPS OSD Menu", 5, 3);
|
460 | //*------------------------------------
|
461 |
|
462 | // find activ menu item
|
463 | uint8_t activItem = 0;
|
464 | while (true) {
|
465 | if (Menu[activItem].flags.MENUEND) {
|
466 | osd.printMax7456Char(0xfC, 5, 7 + 0, true, true);
|
467 | Menu[0].flags.ACTIVEITEM = true;
|
468 | break;
|
469 | }
|
470 | activItem++;
|
471 | }
|
472 |
|
473 | // draw until the end of pointer list
|
474 | uint8_t i = 0;
|
475 | do {
|
476 | osd.print(Menu[i].text, 7, 7+i);
|
477 | i++;
|
478 | } while (!Menu[i-1].flags.MENUEND);
|
479 | _delay_ms(20);
|
480 | }
|
481 |
|
482 | void MenuAction(struct MenuEntry *Menu ){
|
483 | uint8_t activItem = 0;
|
484 |
|
485 | do{
|
486 | if (Menu[activItem].flags.ACTIVEITEM) {
|
487 | // Menu[activItem].function(Menu[activItem].subMenu);
|
488 | drawMenu(ptrMenu);
|
489 | uart_puts_p("hier kommt blödsinn raus ");
|
490 | return;
|
491 | }
|
492 | activItem++;
|
493 | }while (!Menu[activItem - 1 ].flags.MENUEND);
|
494 | }
|
495 |
|
496 | void MenuNext(struct MenuEntry *Menu ){
|
497 | uint8_t activItem = 0;
|
498 |
|
499 | while (true) {
|
500 | if (Menu[activItem].flags.MENUEND && Menu[activItem].flags.ACTIVEITEM) {
|
501 | osd.printMax7456Char(0x00, 5, 7 + activItem);
|
502 | Menu[activItem].flags.ACTIVEITEM = false;
|
503 | MenuRefresh(Menu);
|
504 | return;
|
505 | }
|
506 | if (Menu[activItem].flags.ACTIVEITEM) {
|
507 | Menu[activItem].flags.ACTIVEITEM = false;
|
508 | Menu[activItem + 1].flags.ACTIVEITEM = true;
|
509 | osd.printMax7456Char(0x00, 5, 7 + activItem);
|
510 | osd.printMax7456Char(0xfC, 5, 8 + activItem, true ,true );
|
511 | return;
|
512 | }
|
513 | activItem++;
|
514 | }
|
515 | }
|
516 | void MenuIntervalAction(struct MenuEntry *Menu){
|
517 |
|
518 | // refresh Manager
|
519 | uint8_t activItem = 0;
|
520 | do {
|
521 | // single refresh
|
522 | if (Menu[activItem].flags.ACTIVEITEM) {
|
523 | if (Menu[activItem].flags.REFRESHINTERVAL) {
|
524 | Menu[activItem].function(NULL);
|
525 | }
|
526 | }
|
527 | } while (!Menu[activItem - 1 ].flags.MENUEND);
|
528 | }
|
529 | //******************************************************************************
|
530 | //* Main loop
|
531 | //******************************************************************************
|
532 |
|
533 | int main(void) {
|
534 | // init
|
535 | init();
|
536 | // init menu -> jump to MainMenu
|
537 | ptrMenu = MainMenu;
|
538 | MenuRefresh(ptrMenu);
|
539 | // ready
|
540 | uart_puts("Cyclops init \n\r");
|
541 |
|
542 | for(;;)
|
543 | {
|
544 | // MenuIntervalAction(ptrMenu);
|
545 |
|
546 | switch (getClickType())
|
547 | {
|
548 | case NO_CLICK: // do nothing
|
549 | break;
|
550 |
|
551 | case LONG_CLICK: //
|
552 | MenuAction(ptrMenu);
|
553 | break;
|
554 | case SINGLE_CLICK: //
|
555 | MenuNext(ptrMenu);
|
556 | break;
|
557 |
|
558 | case DOUBLE_CLICK: //
|
559 | break;
|
560 | }
|
561 |
|
562 |
|
563 |
|
564 | }
|
565 |
|
566 | return 0; // never reached
|
567 | }
|