Forum: Mikrocontroller und Digitale Elektronik Atmega328P Receive Interrupt - Code Review


von John P. (brushlesspower)


Lesenswert?

Hallo,

ich habe fertigen Code für den Atmega328P bekommen. Leider ist der Code 
für eine doch recht umständliche Hardware geschrieben.

Ausgangsbasis:

1-Wire Serial wird mit Tri-State buffern auf RX und TX beschaltet. Der 
Code aktiviert dann den TX oder den RX Buffer.
Soweit so gut, der Code funktioniert.

Da mir die Lösung mit den Tristate Buffern nicht gefällt, habe ich die 
Tristate Buffer entfernt. Die 1-Wire leitung hängt nun an RX und über 
einen 4k7 an TX.

Dies funktioniert aber nur eingeschränkt.
Wenn der Atmega daten sendet, werden diese nun auch auf dem RX empfangen 
(Echo). Vorher war hier der RX Buffer hochohmig. Und dadurch das die RX 
Routine etwas empfängt wird das senden unterbrochen. Daher werden nicht 
alle Daten gesendet.

Meine Idee ist es den RX Interrupt während der Transmit Periode zu 
deaktivieren.
Leider kenne ich mit mit AVR's überhaupt nicht aus. Und meine 
Programmier Kenntnisse würde ich eher mit "Grundlagen" beschreiben.

Daher hoffe ich das mir jemand Hinsweise geben kann wie ich den RX 
Handler deaktiviere bzw, dann auch wieder gezielt aktiviere.


Zur Funktion:

Über das 1-Wire interface werden Daten(Servo kanäle) vom RC Empfänger 
zum Atmega übertragen. Danach gibt es ein Zeitfenster (festes Timing) in 
dem Telemetriedaten (Spannung, Temp, usw) an den RC Empfänger übertragen 
werden.
der ganze Code ist in Github hinterlegt:
https://github.com/BrushlessPower/SBUS2-Telemetry

Hier der interessante teil für die UART funktionen.
Ich hoffe Ihr könnt mir helfen.
1
/*
2
This program is free software: you can redistribute it and/or modify
3
it under the terms of the GNU General Public License as published by
4
the Free Software Foundation, either version 3 of the License, or
5
(at your option) any later version.
6
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
GNU General Public License for more details.
11
12
You should have received a copy of the GNU General Public License
13
along with this program.  If not, see <http://www.gnu.org/licenses/>.
14
*
15
*  @ Project : MultiSensor
16
*  @ File Name :SBUS_usart.cpp
17
*  @ Date : 6/12/2013
18
*  @ Author : Bart Keser
19
*
20
*/
21
22
#include <avr/io.h>
23
#include <avr/interrupt.h>
24
#include <stdarg.h>
25
#include <stdlib.h>
26
#include <string.h>
27
28
#include "myavr.h"
29
#include "SBUS_usart.h"
30
#include "usart.h"
31
32
33
#define F_CPU                   8000000
34
35
#define SLOT_DATA_LENGTH        3
36
#define NUMBER_OF_FRAMES        4
37
#define NUMBER_OF_SLOT          32
38
#define NUMBER_OF_SLOT_IN_FRAME 8
39
#define SBUS_FRAME_SIZE         25
40
#define SBUS_CHANNEL_SIZE       11
41
42
43
#define UART_RXBUFSIZE  30
44
#define FRAME_TIME_OUT  200 //ms
45
46
47
#define TRANSMIT_PIN  PIN_0 // digital IO C
48
#define RECEIVE_PIN   PIN_5 // digital IO B
49
#define TRANSMIT_RECEIVE_ENABLE  LOW
50
#define TRANSMIT_RECEIVE_DISABLE HIGH
51
52
#define SLOT_TIME 90    //165
53
#define SLOT_TIME2 165    
54
55
56
uint8_t  swaps_slot_bits[8] = {0,4,2,6,1,5,3,7};
57
   
58
uint8_t toggle  = 0;   
59
60
61
//static bool      command_received = false;
62
//static uint8_t   command[UART_RXBUFSIZE];
63
//static uint8_t   command_length = 9;
64
65
// 248 is around 1 ms
66
uint8_t  transmit_sequence_timer[15] = {250,250,SLOT_TIME2,SLOT_TIME,SLOT_TIME,SLOT_TIME,SLOT_TIME,SLOT_TIME,SLOT_TIME,SLOT_TIME,226,226,226,226,180};
67
uint8_t  receive_timeout_timer = ( uint8_t ) (248.0 * (FRAME_TIME_OUT / 1000.0));
68
uint16_t overflow_counter = 0; // should not occur
69
70
static volatile uint8_t   rxbuf[UART_RXBUFSIZE];
71
static volatile bool      frame_ready = false;
72
static volatile uint8_t   gl_current_frame = 0;
73
static volatile uint16_t  uart_lost_frame = 0;
74
75
typedef enum
76
{
77
   EMPTY = 0,
78
   TRANSMITTING,
79
   AVAILABLE
80
} SLOT_DATA_STATUS;
81
82
typedef struct
83
{
84
   volatile bool    data_status;
85
   volatile uint8_t data[SLOT_DATA_LENGTH];
86
} SLOT_RAW_DATA;
87
88
// low resolution timer
89
volatile uint8_t         frameLength = 15;
90
volatile int8_t          previousFrame = 0;
91
volatile uint32_t        frameCounter = 0;
92
93
94
volatile uint8_t        tx_data_counter;
95
volatile uint8_t        buffer_index;
96
97
volatile SLOT_DATA_STATUS transmit_data_per_slot_status[NUMBER_OF_SLOT];
98
volatile uint8_t          transmit_data_per_slot_data[NUMBER_OF_SLOT][SLOT_DATA_LENGTH];
99
volatile uint8_t          gl_slot;
100
101
void sbus_uart_init();
102
void sbus_timer_init();
103
void start_receiving();
104
void enable_receiving();
105
void enable_transmiting();
106
void disable_receiving();
107
void disable_transmiting();
108
void start_transmit_sequencer(uint8_t frame_number);
109
void sbus2_send_slot(uint8_t slot);
110
111
void ISR_receive_timeout();
112
void ISR_transmit();
113
114
volatile void    (*do_servo_pulls)(uint32_t counter);
115
116
volatile void    (*timer_ISR)();
117
118
//*****************************************************************************
119
//
120
void SBUS2_uart_setup (void (*start_pulse)(uint32_t))
121
{
122
   uint32_t counter  = 640000;
123
   uint8_t  response = 0x00;
124
   
125
   noInterrupts();
126
   sbus_uart_init();
127
   sbus_timer_init();
128
129
   PinBasOutput(RECEIVE_PIN);
130
   PinCasOutput(TRANSMIT_PIN);
131
   PinCasOutput(PIN_4);
132
   
133
   disable_transmiting();
134
   disable_receiving();
135
   
136
   UDR = 0xAA;
137
   response = 0x00;
138
   while(!(UCSRA & (1 << UDRE)));
139
   
140
   while ((counter > 1)  && ( response != 0x55 ))
141
   {
142
     response = UDR;
143
     while ( UCSRA & (1 << RXC) );
144
     counter--;
145
   }      
146
          
147
   do_servo_pulls =  (volatile void    (*)(uint32_t))start_pulse;     
148
   start_receiving();
149
   
150
   if(response != 0x55 )
151
   {
152
      enable_receiving();       
153
   }   
154
   interrupts();
155
   
156
}
157
/*
158
void SBUS2_uart_command_length(uint8_t length )
159
{
160
   command_length = length;   
161
}
162
*/
163
164
void sbus_uart_init()
165
{
166
   // set clock divider
167
#undef BAUD
168
#define BAUD USART_BAUD
169
170
#include <util/setbaud.h>
171
172
   UBRRH = UBRRH_VALUE;
173
   UBRRL = UBRRL_VALUE;
174
175
#if USE_2X
176
   UCSRA |= (1 << U2X);  // enable double speed operation
177
#else
178
   UCSRA &= ~(1 << U2X);  // disable double speed operation
179
#endif
180
181
   // set 8E2
182
   UCSRC = (1 << UCSZ1) | (1 << UCSZ0);
183
   UCSRB &= ~(1 << UCSZ2);
184
185
   UCSRC |= (1 << UPM1);
186
   UCSRC |= (1 << USBS);
187
188
   // flush receive buffer
189
   while ( UCSRA & (1 << RXC) ) UDR;
190
191
   UCSRB |= (1 << RXEN);
192
   UCSRB |= (1 << RXCIE);
193
   UCSRB |= (1 << TXEN);
194
   UCSRB |= (1 << TXCIE);
195
}
196
197
void sbus_timer_init()
198
{
199
   //memset( (void*)transmit_data_per_slot, 0, sizeof(transmit_data_per_slot));
200
   for(uint8_t i = 0; i < 32; i++)
201
   {
202
      transmit_data_per_slot_status[i] = EMPTY;
203
   }
204
205
   TCCR2B = 0x00;        //disable Timer2 while we set it up
206
   TCNT2  = 0;           //Reset Timer 0
207
   TIFR2  = 0x02;        //Timer2 INT Flag Reg: Clear Timer Overflow Flag
208
   TIMSK2 = 0x03;        //Timer2 INT Reg: Timer2 compare A
209
   TCCR2A = 0x02;        //Timer2 Control Reg A: CTC
210
}
211
212
uint8_t SBUS_get_frame(uint8_t *frame)
213
{
214
   if( frame_ready )
215
   {
216
      memcpy(frame, (void*)rxbuf, SBUS_FRAME_SIZE);
217
      frame_ready = false;
218
      return true;
219
   }
220
   else
221
   {
222
      return false;
223
   }
224
}
225
226
void start_receiving()
227
{
228
   TCCR2B = 0x00;        //disable Timer2 while we set it up
229
   buffer_index = 0;
230
   timer_ISR = (volatile void    (*)())ISR_receive_timeout;
231
   TCNT2  = 0;           //Reset Timer 0
232
   OCR2A = receive_timeout_timer; 
233
}
234
235
inline void disable_receiving()
236
{
237
   PinBOutput( RECEIVE_PIN, TRANSMIT_RECEIVE_DISABLE);
238
}
239
240
inline void enable_receiving()
241
{
242
   PinCOutput( TRANSMIT_PIN, TRANSMIT_RECEIVE_DISABLE);
243
   PinBOutput( RECEIVE_PIN, TRANSMIT_RECEIVE_ENABLE);
244
}
245
246
inline void disable_transmiting()
247
{
248
   PinCOutput( TRANSMIT_PIN, TRANSMIT_RECEIVE_DISABLE);
249
}
250
251
inline void enable_transmiting()
252
{
253
   PinBOutput( RECEIVE_PIN, TRANSMIT_RECEIVE_DISABLE);
254
   PinCOutput( TRANSMIT_PIN, TRANSMIT_RECEIVE_ENABLE);
255
}
256
257
ISR(TIMER2_COMPA_vect)
258
{
259
   timer_ISR();
260
}
261
262
inline void IncreaseTimer( int8_t frameNumber)
263
{
264
   int8_t temp = (frameNumber - previousFrame) % NUMBER_OF_FRAMES;
265
   if (temp <= 0 )
266
      temp += NUMBER_OF_FRAMES;          
267
   if (temp > 1)
268
      uart_lost_frame++;
269
   frameCounter += temp;
270
   previousFrame = frameNumber;   
271
}
272
273
ISR (USART_RX_vect)
274
{
275
   uint8_t cdata = 0;
276
   
277
   frame_ready = false;
278
   TCNT2  = 0; //Reset Timer 2 for new usart time of char
279
   //enable Timer2 Control Reg B: for receive timeout this is done here because we can only start the timeout after first bye is received
280
   TCCR2B = 0x03;
281
   cdata = UDR;   
282
   rxbuf[buffer_index] = cdata;
283
   buffer_index++;
284
   if (buffer_index == SBUS_FRAME_SIZE)
285
   {
286
      frame_ready = true;
287
      disable_receiving();
288
      IncreaseTimer((cdata&0x30) >> 4);
289
      start_transmit_sequencer(((cdata&0x30) >> 4)); // frame number needed to select correct telemetry slot
290
291
      if (do_servo_pulls != NULL )
292
      {
293
         do_servo_pulls(frameCounter * frameLength);
294
      }
295
      buffer_index = 0;
296
   } 
297
}
298
299
300
// receive timeout check for set packed length
301
void ISR_receive_timeout()
302
{   
303
/*   if ( buffer_index == command_length )
304
   {
305
      // -1 don't add crc in crc :-)
306
      uint8_t crc = 0;
307
      //crc = crc_cal( (uint8_t*)rxbuf, command_length -1);
308
      
309
      if ( (crc == rxbuf[command_length -1]) || true )
310
      {
311
         disable_receiving(); // this puts the module in setup mode 
312
         // setup command   
313
         memcpy(command, (void*)rxbuf, command_length);    
314
         command_received = true;
315
      }      
316
   }
317
   */
318
   buffer_index = 0;
319
}
320
321
//****************************************************************
322
//* TRANSMIT SEQUENCER
323
//****************************************************************
324
325
void ISR_transmit()
326
{
327
   static  uint8_t sequence_count = 1; // first sequence step delay will be filled in when the transmit sequence is enabled
328
   //Increments the interrupt counter
329
   TCNT2  = 0; // reset at the beginning so that  there is no delay for the next segment of the sequence
330
331
   interrupts();
332
333
   if (sequence_count < 2 )
334
   {
335
      // don't do anything this is delay to the transmission slots
336
      OCR2A = transmit_sequence_timer[sequence_count];
337
   }
338
   else if (sequence_count < 10 ) // transmit slots
339
   {
340
      OCR2A = transmit_sequence_timer[sequence_count]; // first set next slot interrupt
341
      sbus2_send_slot((sequence_count-2) + (gl_current_frame * NUMBER_OF_SLOT_IN_FRAME) );
342
   }
343
   else if (sequence_count < 11 )
344
   {
345
      OCR2A = transmit_sequence_timer[sequence_count];
346
347
   }
348
   else if (sequence_count < 14 )
349
   {
350
      // delay to enabling receive again
351
      OCR2A = transmit_sequence_timer[sequence_count];
352
   }
353
   else if (sequence_count < 15 )
354
   {
355
      // delay to enabling receive again
356
      OCR2A = transmit_sequence_timer[sequence_count];
357
      frame_ready = false;  // this will give  ms to collect the servo data
358
   }
359
   else
360
   {
361
      // reset transmit sequencer
362
      TCCR2B = 0x00;        //Disbale Timer2 while we set it up
363
      sequence_count = 0;  // first sequence step delay will be filled in when the transmit sequence is enabled
364
365
      enable_receiving();
366
      start_receiving();
367
   }
368
   sequence_count++;
369
370
   TIFR2 = 0x00;          //Timer2 INT Flag Reg: Clear Timer Overflow Flag
371
};
372
373
ISR(TIMER2_OVF_vect)
374
{
375
   // for debugging should not occur
376
   overflow_counter++;
377
};
378
379
380
void start_transmit_sequencer(uint8_t frame_number)
381
{
382
   //set transmit ISR
383
   timer_ISR = (volatile void    (*)())ISR_transmit;
384
385
   OCR2A  = transmit_sequence_timer[0]; //set first delay value
386
   TCCR2B = 0x03;                       //enable Timer2 Control Reg B: Timer Prescaler set to 128
387
388
   gl_current_frame = frame_number;
389
}
390
391
392
//****************************************************************
393
//* TRANSMIT usart send
394
//****************************************************************
395
void sbus2_send_slot(uint8_t slot)
396
{
397
   // if data available in slot then send it
398
   if ( transmit_data_per_slot_status[slot] == AVAILABLE )
399
   {
400
      
401
      transmit_data_per_slot_status[slot] = TRANSMITTING;
402
      enable_transmiting();
403
      //sending_slot = &transmit_data_per_slot[slot];
404
      gl_slot = slot;
405
      //send first byte
406
      UDR = transmit_data_per_slot_data[gl_slot][0]; // the reset will be done by TX ISR
407
      tx_data_counter = 1;
408
   }
409
}
410
411
ISR (USART_TX_vect)
412
{
413
   interrupts();
414
   if (transmit_data_per_slot_status[gl_slot] != EMPTY) 
415
   {
416
      if ( tx_data_counter < SLOT_DATA_LENGTH ) 
417
      {
418
         UDR = transmit_data_per_slot_data[gl_slot][tx_data_counter];
419
         tx_data_counter++;
420
      }
421
      else
422
      {
423
         // disable transmitter line and set data status to empty so that it can be written again
424
         disable_transmiting();
425
         transmit_data_per_slot_status[gl_slot] = EMPTY;
426
      }
427
   }   
428
}
429
430
void SBUS2_transmit_telemetry_data( uint8_t slot, uint8_t data[SLOT_DATA_LENGTH] )
431
{
432
   uint8_t swapped_slot = 0;
433
   if ( transmit_data_per_slot_status[slot] != TRANSMITTING )
434
   {
435
      //swap slot ID
436
      swapped_slot = swaps_slot_bits[slot % 8] << 5;
437
      memcpy( (void*)transmit_data_per_slot_data[slot], data, SLOT_DATA_LENGTH);
438
      transmit_data_per_slot_data[slot][0] &= 0x1F; // reset frame slot ID
439
      transmit_data_per_slot_data[slot][0] |= swapped_slot;
440
441
      transmit_data_per_slot_status[slot] = AVAILABLE;
442
   }
443
}
444
445
bool SBUS2_get_all_servo_data( uint16_t channels[16] )
446
{
447
   if ( frame_ready )
448
   {
449
      uint8_t byte_in_sbus = 1;
450
      uint16_t bit_in_sbus = 0;
451
      uint8_t ch = 0;
452
      uint16_t bit_in_channel = 0;
453
      //uint16_t temp;
454
      uint8_t channel_data[UART_RXBUFSIZE];
455
      //noInterrupts();
456
      memcpy(channel_data, (void*)rxbuf, 24);
457
      //interrupts();
458
459
      for (uint8_t i=0; i<16; i++)
460
         channels[i] = 0;
461
462
      // process actual sbus data
463
      for (uint8_t i=0; i<176; i++)
464
      {
465
         //temp = 1<<bit_in_sbus;
466
         if (channel_data[byte_in_sbus] & (1<<bit_in_sbus))
467
         {
468
            //temp = (1<<bit_in_channel);
469
            channels[ch] |= (1<<bit_in_channel);
470
         }
471
         bit_in_sbus++;
472
         bit_in_channel++;
473
474
         if (bit_in_sbus == 8)
475
         {
476
            bit_in_sbus =0;
477
            byte_in_sbus++;
478
         }
479
         if (bit_in_channel == 11)
480
         {
481
            bit_in_channel =0;
482
            ch++;
483
         }
484
      }
485
      // DigiChannel 1
486
      if (channel_data[23] & (1<<0))
487
      {
488
         channels[16] = 1;
489
      }
490
      else
491
      {
492
         channels[16] = 0;
493
      }
494
      // DigiChannel 2
495
      if (channel_data[23] & (1<<1))
496
      {
497
         channels[17] = 1;
498
      }
499
      else
500
      {
501
         channels[17] = 0;
502
      }
503
504
      return true;
505
   }
506
   else
507
   {
508
      return false;
509
   }
510
}
511
512
513
void SBUS2_get_status( uint16_t *uart_dropped_frame, bool *transmision_dropt_frame, bool *failsave )
514
{
515
   if (frameCounter < 60)
516
   {
517
      uart_lost_frame = 0;
518
   }
519
   *uart_dropped_frame = uart_lost_frame;
520
   *transmision_dropt_frame = rxbuf[23] & 0x20 ? true : false;
521
   *failsave = rxbuf[23] & 0x10 ? true : false;
522
}
523
524
/*
525
bool SBUS2_get_command( uint8_t   *p_command )
526
{   
527
   if (command_received)
528
   {  
529
      memcpy(p_command, command, command_length);       
530
      command_received = false;   
531
532
      return true;
533
   }
534
   return false;
535
}
536
537
538
void SBUS2_send_command( uint8_t   *p_command )
539
{
540
   int index = 0;   
541
   
542
   for( index = 0; index < command_length; index++)
543
   {
544
      while( !(UCSRA & (1<<UDRE))){}   
545
      UDR = p_command[index];      
546
   } 
547
}
548
*/   
549
550
int16_t SBUS2_get_servo_data( uint8_t channel )
551
{
552
   uint8_t byte_in_sbus = 1;
553
   uint16_t bit_in_sbus = 0;
554
   uint8_t ch = 0;
555
   uint16_t bit_in_channel = 0;
556
   uint8_t channel_data[UART_RXBUFSIZE];
557
558
   uint16_t  servo = 0;
559
   uint8_t  start_bit  = 0;
560
561
   //noInterrupts();
562
   memcpy(channel_data, (void*)rxbuf, 24);
563
   //interrupts();
564
565
   if ( frame_ready )
566
   {
567
      start_bit  = channel * SBUS_CHANNEL_SIZE ;
568
      bit_in_sbus  = start_bit % 8;
569
      byte_in_sbus = (start_bit / 8) + 1;
570
571
      // process actual sbus data
572
      for (uint8_t i=start_bit; i<(start_bit+11); i++)
573
      {
574
         if (channel_data[byte_in_sbus] & (1<<bit_in_sbus))
575
         {
576
            servo |= (1<<bit_in_channel);
577
         }
578
         bit_in_sbus++;
579
         bit_in_channel++;
580
581
         if (bit_in_sbus == 8)
582
         {
583
            bit_in_sbus =0;
584
            byte_in_sbus++;
585
         }
586
         if (bit_in_channel == 11)
587
         {
588
            bit_in_channel =0;
589
            ch++;
590
         }
591
      }
592
      return (int16_t)servo;
593
   }
594
   else
595
   {
596
      return -1;
597
   }
598
}

von Linux T. (Gast)


Lesenswert?

Was an:
"Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang"
hast Du nicht verstanden?

von H.Joachim S. (crazyhorse)


Lesenswert?

John P. schrieb:
> Dies funktioniert aber nur eingeschränkt.
> Wenn der Atmega daten sendet, werden diese nun auch auf dem RX empfangen
> (Echo). Vorher war hier der RX Buffer hochohmig. Und dadurch das die RX
> Routine etwas empfängt wird das senden unterbrochen. Daher werden nicht
> alle Daten gesendet.
>
> Meine Idee ist es den RX Interrupt während der Transmit Periode zu
> deaktivieren.

Eigentlich ist doch so alles richtig, hast du gleich ne Kontrolle, ob 
alles zumindest erst mal korrekt gesendet wurde, bei one-wire sind 
natürlich Kollisionen möglich.
Du sendest z.B. 10 Byte und solltest dann auch diese 10Byte empfangen 
haben. Stimmen sie mit den gesendetetn Daten überein: alles gut, 
empfangene wegwerfen.

von John P. (brushlesspower)


Lesenswert?

H.Joachim S. schrieb:
> Du sendest z.B. 10 Byte und solltest dann auch diese 10Byte empfangen
> haben. Stimmen sie mit den gesendetetn Daten überein: alles gut,
> empfangene wegwerfen.

H.Joachim S. schrieb:
> Eigentlich ist doch so alles richtig, hast du gleich ne Kontrolle, ob
> alles zumindest erst mal korrekt gesendet wurde, bei one-wire sind
> natürlich Kollisionen möglich.

Das Problem ist aber das er das senden unterbricht sobald was am RX 
passiert.

Und das senden muss in einem bestimmten zeitfenster erfolgen.
deswegen muss der RX Interrupt Handler deaktiviert werden.

von John P. (brushlesspower)


Lesenswert?

Linux T. schrieb:
> Was an:
> "Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang"
> hast Du nicht verstanden?

Als wenn da einer reinguckt.

von John P. (brushlesspower)


Lesenswert?

Problem konnte gelöst werden.

Ein Kumpel der hier mitliest hat mir den richtigen Hinweis gegeben.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.