USI_UART.c


1
/*****************************************************************************
2
*
3
* Copyright (C) 2003 Atmel Corporation
4
*
5
* File              : USI_UART.c
6
* Compiler          : IAR EWAAVR 2.28a
7
* Created           : 18.07.2002 by JLL
8
* Modified          : 02-10-2003 by LTA
9
*
10
* Support mail      : avr@atmel.com
11
*
12
* Supported devices : ATtiny26
13
*
14
* Application Note  : AVR307 - Half duplex UART using the USI Interface
15
*
16
* Description       : Functions for USI_UART_receiver and USI_UART_transmitter.
17
*                     Uses Pin Change Interrupt to detect incomming signals.
18
*
19
*
20
****************************************************************************/
21
    
22
//#include <ioavr.h>  // Note there is a bug in ioavr.h file that includes iotiny22.h instead of iotiny26.h.
23
//#include <inavr.h>
24
#include "USI_UART.h"
25
26
27
//********** USI UART Defines **********//
28
29
#define DATA_BITS                 8
30
#define START_BIT                 1
31
#define STOP_BIT                  1
32
#define HALF_FRAME                5
33
34
#define USI_COUNTER_MAX_COUNT     16
35
#define USI_COUNTER_SEED_TRANSMIT (USI_COUNTER_MAX_COUNT - HALF_FRAME)
36
#define INTERRUPT_STARTUP_DELAY   (0x11 / TIMER_PRESCALER)
37
//#define TIMER0_SEED               (256 - ( (SYSTEM_CLOCK / BAUDRATE) / TIMER_PRESCALER ))
38
#define TIMER0_SEED               (256 - ( (SYSTEM_CLOCK / BAUDRATE) / TIMER_PRESCALER ))+7
39
40
#if ( (( (SYSTEM_CLOCK / BAUDRATE) / TIMER_PRESCALER ) * 3/2) > (256 - INTERRUPT_STARTUP_DELAY) )
41
    #define INITIAL_TIMER0_SEED       ( 256 - (( (SYSTEM_CLOCK / BAUDRATE) / TIMER_PRESCALER ) * 1/2) )
42
    #define USI_COUNTER_SEED_RECEIVE  ( USI_COUNTER_MAX_COUNT - (START_BIT + DATA_BITS) )
43
#else
44
    #define INITIAL_TIMER0_SEED       ( 256 - (( (SYSTEM_CLOCK / BAUDRATE) / TIMER_PRESCALER ) * 3/2) )
45
    #define USI_COUNTER_SEED_RECEIVE  (USI_COUNTER_MAX_COUNT - DATA_BITS)
46
#endif
47
48
#define UART_RX_BUFFER_MASK ( UART_RX_BUFFER_SIZE - 1 )
49
#if ( UART_RX_BUFFER_SIZE & UART_RX_BUFFER_MASK )
50
    #error RX buffer size is not a power of 2
51
#endif
52
53
#define UART_TX_BUFFER_MASK ( UART_TX_BUFFER_SIZE - 1 )
54
#if ( UART_TX_BUFFER_SIZE & UART_TX_BUFFER_MASK )
55
    #error TX buffer size is not a power of 2
56
#endif
57
58
/* General defines */
59
#define TRUE                      1
60
#define FALSE                     0
61
62
//********** Static Variables **********//
63
64
//__no_init __regvar static unsigned char USI_UART_TxData @ 15;   // Tells the compiler to store the byte to be transmitted in registry.
65
static unsigned char USI_UART_TxData;
66
67
static unsigned char          UART_RxBuf[UART_RX_BUFFER_SIZE];  // UART buffers. Size is definable in the header file.
68
static volatile unsigned char UART_RxHead;
69
static volatile unsigned char UART_RxTail;
70
static unsigned char          UART_TxBuf[UART_TX_BUFFER_SIZE];
71
static volatile unsigned char UART_TxHead;
72
static volatile unsigned char UART_TxTail;
73
74
static volatile union USI_UART_status                           // Status byte holding flags.
75
{
76
    unsigned char status;
77
    struct
78
    {
79
        unsigned char ongoing_Transmission_From_Buffer:1;
80
        unsigned char ongoing_Transmission_Of_Package:1;
81
        unsigned char ongoing_Reception_Of_Package:1;
82
        unsigned char reception_Buffer_Overflow:1;
83
        unsigned char flag4:1;
84
        unsigned char flag5:1;
85
        unsigned char flag6:1;
86
        unsigned char flag7:1;
87
    };
88
} USI_UART_status = {0};
89
90
91
//********** USI_UART functions **********//
92
93
// Reverses the order of bits in a byte.
94
// I.e. MSB is swapped with LSB, etc.
95
unsigned char Bit_Reverse( unsigned char x )
96
{
97
    x = ((x >> 1) & 0x55) | ((x << 1) & 0xaa);
98
    x = ((x >> 2) & 0x33) | ((x << 2) & 0xcc);
99
    x = ((x >> 4) & 0x0f) | ((x << 4) & 0xf0);
100
    return x;    
101
}
102
103
// Flush the UART buffers.
104
void USI_UART_Flush_Buffers( void )  
105
{  
106
    UART_RxTail = 0;
107
    UART_RxHead = 0;
108
    UART_TxTail = 0;
109
    UART_TxHead = 0;
110
}
111
112
// Initialise USI for UART transmission.
113
void USI_UART_Initialise_Transmitter( void )                              
114
{
115
    cli();
116
    TCNT0  = 0x00;  //Zähler0 auf 0 setzen
117
  TCCR0B = (0<<CS02)|(0<<CS01)|(1<<CS00);              // Reset the prescaler and start Timer0.
118
    TIFR   = (1<<TOV0);                                       // Clear Timer0 OVF interrupt flag.
119
    TIMSK |= (1<<TOIE0);                                      // Enable Timer0 OVF interrupt.
120
                                                                
121
    USICR  = (0<<USISIE)|(1<<USIOIE)|                         // Enable USI Counter OVF interrupt.
122
             (0<<USIWM1)|(1<<USIWM0)|                         // Select Three Wire mode.
123
             (0<<USICS1)|(1<<USICS0)|(0<<USICLK)|             // Select Timer0 OVER as USI Clock source.
124
             (0<<USITC);                                           
125
             
126
    USIDR  = 0xFF;                                            // Make sure MSB is '1' before enabling USI_DO.
127
    USISR  = 0xF0 |                                           // Clear all USI interrupt flags.
128
             0x0F;                                            // Preload the USI counter to generate interrupt at first USI clock.
129
    DDRB  |= (1<<PB1);                                        // Configure USI_DO as output.
130
                  
131
    USI_UART_status.ongoing_Transmission_From_Buffer = TRUE;
132
                  
133
    sei();
134
}
135
136
// Initialise USI for UART reception.
137
// Note that this function only enables pinchange interrupt on the USI Data Input pin.
138
// The USI is configured to read data within the pinchange interrupt.
139
void USI_UART_Initialise_Receiver( void )        
140
{  
141
    PORTB |=   (1<<PB3)|(1<<PB2)|(1<<PB1)|(1<<PB0);         // Enable pull up on USI DO, DI and SCK pins. (And PB3 because of pin change interrupt)   
142
    DDRB  &= ~((1<<PB3)|(1<<PB2)|(1<<PB1)|(1<<PB0));        // Set USI DI, DO and SCK pins as inputs.  
143
    USICR  =  0;                                            // Disable USI.
144
    GIFR   =  (1<<PCIF);                                    // Clear pin change interrupt flag.
145
    GIMSK |=  (1<<PCIE);                                    // Enable pin change interrupt for PB3:0.
146
    PCMSK |=  (1<<PCINT0)|(1<<PCINT1)|(1<<PCINT2)|(1<<PCINT3);          // Enable pin change interrupt for PB3:0.
147
}
148
149
// Puts data in the transmission buffer, after reverseing the bits in the byte.
150
// Initiates the transmission rutines if not already started.
151
void USI_UART_Transmit_Byte( unsigned char data )          
152
{
153
    unsigned char tmphead;
154
155
    tmphead = ( UART_TxHead + 1 ) & UART_TX_BUFFER_MASK;        // Calculate buffer index.
156
    while ( tmphead == UART_TxTail );                           // Wait for free space in buffer.
157
    UART_TxBuf[tmphead] = Bit_Reverse(data);                    // Reverse the order of the bits in the data byte and store data in buffer.
158
    UART_TxHead = tmphead;                                      // Store new index.
159
    
160
    if ( !USI_UART_status.ongoing_Transmission_From_Buffer )    // Start transmission from buffer (if not already started).
161
    {
162
        while ( USI_UART_status.ongoing_Reception_Of_Package ); // Wait for USI to finsh reading incoming data.
163
        USI_UART_Initialise_Transmitter();              
164
    }
165
}
166
167
// Returns a byte from the receive buffer. Waits if buffer is empty.
168
unsigned char USI_UART_Receive_Byte( void )                
169
{
170
    unsigned char tmptail;
171
        
172
    while ( UART_RxHead == UART_RxTail );                 // Wait for incomming data 
173
    tmptail = ( UART_RxTail + 1 ) & UART_RX_BUFFER_MASK;  // Calculate buffer index 
174
    UART_RxTail = tmptail;                                // Store new index 
175
    return Bit_Reverse(UART_RxBuf[tmptail]);              // Reverse the order of the bits in the data byte before it returns data from the buffer.
176
}
177
178
// Check if there is data in the receive buffer.
179
unsigned char USI_UART_Data_In_Receive_Buffer( void )        
180
{
181
    return ( UART_RxHead != UART_RxTail );                // Return 0 (FALSE) if the receive buffer is empty.
182
}
183
184
185
// ********** Interrupt Handlers ********** //
186
187
// The pin change interrupt is used to detect USI_UART reseption.
188
// It is here the USI is configured to sample the UART signal.
189
ISR(SIG_PIN_CHANGE)
190
{                                                                    
191
    if (!( PINB & (1<<PB0) ))                                     // If the USI DI pin is low, then it is likely that it
192
    {                                                             //  was this pin that generated the pin change interrupt.
193
        TCNT0  = INTERRUPT_STARTUP_DELAY + INITIAL_TIMER0_SEED;   // Plant TIMER0 seed to match baudrate (incl interrupt start up time.).
194
        TCCR0B  = (0<<CS02)|(0<<CS01)|(1<<CS00);              // Reset the prescaler and start Timer0.
195
        TIFR   = (1<<TOV0);                                       // Clear Timer0 OVF interrupt flag.
196
        TIMSK |= (1<<TOIE0);                                      // Enable Timer0 OVF interrupt.
197
                                                                    
198
        USICR  = (0<<USISIE)|(1<<USIOIE)|                         // Enable USI Counter OVF interrupt.
199
                 (0<<USIWM1)|(1<<USIWM0)|                         // Select Three Wire mode.
200
                 (0<<USICS1)|(1<<USICS0)|(0<<USICLK)|             // Select Timer0 OVER as USI Clock source.
201
                 (0<<USITC);                                           
202
                                                                  // Note that enabling the USI will also disable the pin change interrupt.
203
        USISR  = 0xF0 |                                           // Clear all USI interrupt flags.
204
                 USI_COUNTER_SEED_RECEIVE;                        // Preload the USI counter to generate interrupt.
205
                                                                  
206
        GIMSK &=  ~(1<<PCIE);                                     // Disable pin change interrupt for PB3:0. 
207
        
208
        USI_UART_status.ongoing_Reception_Of_Package = TRUE;             
209
    }
210
}
211
212
// The USI Counter Overflow interrupt is used for moving data between memmory and the USI data register.
213
// The interrupt is used for both transmission and reception.
214
ISR(SIG_USI_OVERFLOW)
215
{
216
    unsigned char tmphead,tmptail;
217
    
218
    // Check if we are running in Transmit mode.
219
    if( USI_UART_status.ongoing_Transmission_From_Buffer )      
220
    {
221
        // If ongoing transmission, then send second half of transmit data.
222
        if( USI_UART_status.ongoing_Transmission_Of_Package )   
223
        {                                   
224
            USI_UART_status.ongoing_Transmission_Of_Package = FALSE;    // Clear on-going package transmission flag.
225
            
226
            USISR = 0xF0 | (USI_COUNTER_SEED_TRANSMIT);                 // Load USI Counter seed and clear all USI flags.
227
            USIDR = (USI_UART_TxData << 3) | 0x07;                      // Reload the USIDR with the rest of the data and a stop-bit.
228
        }
229
        // Else start sending more data or leave transmit mode.
230
        else
231
        {
232
            // If there is data in the transmit buffer, then send first half of data.
233
            if ( UART_TxHead != UART_TxTail )                           
234
            {
235
                USI_UART_status.ongoing_Transmission_Of_Package = TRUE; // Set on-going package transmission flag.
236
                
237
                tmptail = ( UART_TxTail + 1 ) & UART_TX_BUFFER_MASK;    // Calculate buffer index.
238
                UART_TxTail = tmptail;                                  // Store new index.            
239
                USI_UART_TxData = UART_TxBuf[tmptail];                  // Read out the data that is to be sent. Note that the data must be bit reversed before sent.
240
                                                                        // The bit reversing is moved to the application section to save time within the interrupt.
241
                USISR  = 0xF0 | (USI_COUNTER_SEED_TRANSMIT);            // Load USI Counter seed and clear all USI flags.
242
                USIDR  = (USI_UART_TxData >> 2) | 0x80;                 // Copy (initial high state,) start-bit and 6 LSB of original data (6 MSB
243
                                                                        //  of bit of bit reversed data).                
244
            }
245
            // Else enter receive mode.
246
            else
247
            {
248
                USI_UART_status.ongoing_Transmission_From_Buffer = FALSE; 
249
                
250
                TCCR0B  = (0<<CS02)|(0<<CS01)|(0<<CS00);                // Stop Timer0.
251
                PORTB |=   (1<<PB3)|(1<<PB2)|(1<<PB1)|(1<<PB0);         // Enable pull up on USI DO, DI and SCK pins. (And PB3 because of pin change interrupt)   
252
                DDRB  &= ~((1<<PB3)|(1<<PB2)|(1<<PB1)|(1<<PB0));        // Set USI DI, DO and SCK pins as inputs.  
253
                USICR  =  0;                                            // Disable USI.
254
                GIFR   =  (1<<PCIF);                                    // Clear pin change interrupt flag.
255
                GIMSK |=  (1<<PCIE);                                    // Enable pin change interrupt for PB3:0.
256
          PCMSK |=  (1<<PCINT0)|(1<<PCINT1)|(1<<PCINT2)|(1<<PCINT3);          // Enable pin change interrupt for PB3:0.
257
            }
258
        }
259
    }
260
    
261
    // Else running in receive mode.
262
    else                                                                
263
    {              
264
        USI_UART_status.ongoing_Reception_Of_Package = FALSE;           
265
266
        tmphead     = ( UART_RxHead + 1 ) & UART_RX_BUFFER_MASK;        // Calculate buffer index.
267
        
268
        if ( tmphead == UART_RxTail )                                   // If buffer is full trash data and set buffer full flag.
269
        {
270
            USI_UART_status.reception_Buffer_Overflow = TRUE;           // Store status to take actions elsewhere in the application code
271
        }
272
        else                                                            // If there is space in the buffer then store the data.
273
        {
274
            UART_RxHead = tmphead;                                      // Store new index.
275
            UART_RxBuf[tmphead] = USIDR;                                // Store received data in buffer. Note that the data must be bit reversed before used. 
276
        }                                                               // The bit reversing is moved to the application section to save time within the interrupt.
277
                                                                
278
        TCCR0B  = (0<<CS02)|(0<<CS01)|(0<<CS00);                // Stop Timer0.
279
        PORTB |=   (1<<PB3)|(1<<PB2)|(1<<PB1)|(1<<PB0);         // Enable pull up on USI DO, DI and SCK pins. (And PB3 because of pin change interrupt)   
280
        DDRB  &= ~((1<<PB3)|(1<<PB2)|(1<<PB1)|(1<<PB0));        // Set USI DI, DO and SCK pins as inputs.  
281
        USICR  =  0;                                            // Disable USI.
282
        GIFR   =  (1<<PCIF);                                    // Clear pin change interrupt flag.
283
        GIMSK |=  (1<<PCIE);                                   // Enable pin change interrupt for PB3:0.
284
      PCMSK |=  (1<<PCINT0)|(1<<PCINT1)|(1<<PCINT2)|(1<<PCINT3);          // Enable pin change interrupt for PB3:0.
285
    }
286
    
287
}
288
// Timer0 Overflow interrupt is used to trigger the sampling of signals on the USI ports.
289
ISR(SIG_OVERFLOW0)
290
{
291
    TCNT0 += TIMER0_SEED;                   // Reload the timer,
292
                                            // current count is added for timing correction.
293
}