USI_UART.c


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