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 | }
|