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