1 | /*
|
2 | * FreeRTOS serial port driver for ATMega128 and AT90CAN128
|
3 | * Supports UATS0 and UART1 of the MCU (at same time)
|
4 | * Fully interrupt driven.
|
5 | * Based on implementation for ATMega323
|
6 | * Jonas Mitschang - jonen@mitschang.net
|
7 | */
|
8 | #include <stdlib.h>
|
9 | #include <avr/interrupt.h>
|
10 | #include "FreeRTOS.h"
|
11 | #include "queue.h"
|
12 | #include "task.h"
|
13 | #include "serial.h"
|
14 |
|
15 | // Constants for writing to UCSRC
|
16 | #define serEIGHT_DATA_BITS 0x06
|
17 |
|
18 | xComPort xPortStatus[2];
|
19 |
|
20 | // Macros for enabling and disabling interrupts
|
21 | #define vInterruptOn(x) { \
|
22 | if ((x)==0) { \
|
23 | UCSR0B |= (1<<UDRIE0); \
|
24 | } else { \
|
25 | UCSR1B |= (1<<UDRIE1); \
|
26 | } }
|
27 | #define vInterruptOff(x) { \
|
28 | if ((x)==0) { \
|
29 | UCSR0B &= ~(1<<UDRIE0); \
|
30 | } else { \
|
31 | UCSR1B &= ~(1<<UDRIE1); \
|
32 | } }
|
33 | #define vInterruptOnHandle(x) vInterruptOn(((xComPort *)(x))->sPort == serCOM2)
|
34 | #define vInterruptOffHandle(x) vInterruptOff(((xComPort *)(x))->sPort == serCOM2)
|
35 |
|
36 | /**
|
37 | * Initialize serial port
|
38 | * @param ePort may be serCOM1 to use UART0 or serCOM2 to use UART1
|
39 | */
|
40 | xComPortHandle xSerialPortInit(eCOMPort ePort, eBaud eWantedBaud,
|
41 | eParity eWantedParity, eDataBits eWantedDataBits,
|
42 | eStopBits eWantedStopBits, unsigned portBASE_TYPE uxQueueLength) {
|
43 | (void)eWantedDataBits; // prevent warnings
|
44 | (void)eWantedStopBits; // prevent warnings
|
45 | (void)eWantedParity; // prevent warnings
|
46 |
|
47 | unsigned portLONG ulBaudRateCounter;
|
48 |
|
49 | // Port struct
|
50 | xComPort *port = &xPortStatus[ePort==serCOM1?0:1];
|
51 | port->sPort = ePort;
|
52 |
|
53 | // Calculate the baud rate register value from the equation in data sheet
|
54 | switch (eWantedBaud) {
|
55 | case ser9600:
|
56 | ulBaudRateCounter = (configCPU_CLOCK_HZ / 16 / 9600) - 1;
|
57 | break;
|
58 | case ser19200:
|
59 | ulBaudRateCounter = (configCPU_CLOCK_HZ / 16 / 19200) - 1;
|
60 | break;
|
61 | case ser38400:
|
62 | ulBaudRateCounter = (configCPU_CLOCK_HZ / 16 / 38400) - 1;
|
63 | break;
|
64 | case ser57600:
|
65 | ulBaudRateCounter = (configCPU_CLOCK_HZ / 16 / 57600) - 1;
|
66 | break;
|
67 | case ser115200:
|
68 | ulBaudRateCounter = (configCPU_CLOCK_HZ / 16 / 115200) - 1;
|
69 | break;
|
70 | default:
|
71 | // unknown baud rate => throw error
|
72 | return NULL;
|
73 | // unsigned portLONG ulWantedBaud = 38400;
|
74 | // ulBaudRateCounter =
|
75 | // (configCPU_CLOCK_HZ / (16*ulWantedBaud)) - (unsigned portLONG)1;
|
76 | }
|
77 |
|
78 | // Create the queues
|
79 | portENTER_CRITICAL();
|
80 | port->xRxedChars = xQueueCreate(uxQueueLength, (unsigned portBASE_TYPE) sizeof(signed portCHAR));
|
81 | port->xCharsForTx = xQueueCreate(uxQueueLength, (unsigned portBASE_TYPE) sizeof(signed portCHAR));
|
82 |
|
83 | // Set the baud rate. Enable the Rx interrupt, Tx interrupt will get enabled
|
84 | // later. Also enable the Rx and Tx and set the data bits to 8.
|
85 | if (ePort == serCOM1) {
|
86 | UBRR0L = ulBaudRateCounter;
|
87 | UBRR0H = ulBaudRateCounter >> 8;
|
88 | UCSR0B = (1<<RXCIE0) | (1<<RXEN0) | (1<<TXEN0);
|
89 | UCSR0C = (1<<UCSZ01) | (1<<UCSZ00); // TODO: different bit-length
|
90 | } else {
|
91 | UBRR1L = ulBaudRateCounter;
|
92 | UBRR1H = ulBaudRateCounter >> 8;
|
93 | UCSR1B = (1<<RXCIE1) | (1<<RXEN1) | (1<<TXEN1);
|
94 | UCSR1C = (1<<UCSZ11) | (1<<UCSZ10);
|
95 | }
|
96 | portEXIT_CRITICAL();
|
97 | return port;
|
98 | }
|
99 |
|
100 | /**
|
101 | * Used in demo application.
|
102 | * Sometimes ulWantedBaud and sometimes eWantedBaud?!?
|
103 | */
|
104 | xComPortHandle xSerialPortInitMinimal(unsigned portLONG ulWantedBaud, unsigned portBASE_TYPE uxQueueLength ) {
|
105 | (void)ulWantedBaud;
|
106 | return xSerialPortInit(serCOM2, ser38400, serNO_PARITY, serBITS_8, serSTOP_1, uxQueueLength);
|
107 | }
|
108 |
|
109 | /**
|
110 | * Get the next character from the buffer
|
111 | * @return false if no characters are available, or arrive before xBlockTime expires.
|
112 | */
|
113 | signed portBASE_TYPE xSerialGetChar(xComPortHandle pxPort,
|
114 | signed portCHAR *pcRxedChar, portTickType xBlockTime) {
|
115 | if (xQueueReceive(((xComPort *)pxPort)->xRxedChars, pcRxedChar, xBlockTime)) {
|
116 | return pdTRUE;
|
117 | } else {
|
118 | return pdFALSE;
|
119 | }
|
120 | }
|
121 |
|
122 | /**
|
123 | * Write character to send-queue
|
124 | * @return false if after the block time there is no room on the Tx queue
|
125 | */
|
126 | signed portBASE_TYPE xSerialPutChar(xComPortHandle pxPort, signed portCHAR cOutChar, portTickType xBlockTime) {
|
127 | //if (xQueueSend(xPortStatus[1].xCharsForTx, &cOutChar, xBlockTime ) != pdPASS) {
|
128 | if (xQueueSend(((xComPort *)pxPort)->xCharsForTx, &cOutChar, xBlockTime ) != pdPASS) {
|
129 | return pdFAIL;
|
130 | }
|
131 | vInterruptOnHandle(pxPort);
|
132 | return pdPASS;
|
133 | }
|
134 |
|
135 |
|
136 | /**
|
137 | * Write a whole string to send-queue
|
138 | */
|
139 | void vSerialPutString( xComPortHandle pxPort, signed portCHAR * pcString, portSHORT usStringLength )
|
140 | //IM ORIGINAL: void vSerialPutString( xComPortHandle pxPort, const portCHAR * const pcString, unsigned portSHORT usStringLength )
|
141 | {
|
142 | portCHAR * pcNextChar;
|
143 | const portTickType xNoBlock = ( portTickType ) 0;
|
144 |
|
145 | /* Stop warnings. */
|
146 | ( void ) usStringLength;
|
147 |
|
148 | pcNextChar = ( portCHAR * ) pcString;
|
149 | while( *pcNextChar )
|
150 | {
|
151 | xSerialPutChar( pxPort, *pcNextChar, xNoBlock );
|
152 | pcNextChar++;
|
153 | }
|
154 | }
|
155 |
|
156 |
|
157 | /**
|
158 | * Close serial port. Turn off the interrupts.
|
159 | * TODO: We may also want to delete the queues (critical
|
160 | * section!) and/or re-install the original ISR.
|
161 | */
|
162 | void vSerialClose(xComPortHandle xPort) {
|
163 | // TX int
|
164 | vInterruptOffHandle(xPort);
|
165 | // RX int
|
166 | if (((xComPort *)xPort)->sPort == serCOM1) {
|
167 | UCSR0B &= ~(1<<RXCIE0);
|
168 | } else {
|
169 | UCSR1B &= ~(1<<RXCIE0);
|
170 | }
|
171 | }
|
172 |
|
173 | ///////////////////////////////////////////////////////////////////////////////
|
174 | // Interrupts
|
175 | ///////////////////////////////////////////////////////////////////////////////
|
176 | // Get the character and post it on the queue of Rxed characters.
|
177 | // If the post causes a task to wake force a context switch as the woken task
|
178 | // may have a higher priority than the task we have interrupted.
|
179 | SIGNAL(SIG_UART0_RECV) {
|
180 | signed portCHAR cChar;
|
181 | cChar = UDR0;
|
182 | if (xQueueSendFromISR(xPortStatus[0].xRxedChars, &cChar, pdFALSE)) {
|
183 | taskYIELD();
|
184 | }
|
185 | }
|
186 | SIGNAL(SIG_UART1_RECV) {
|
187 | signed portCHAR cChar;
|
188 | cChar = UDR1;
|
189 | if (xQueueSendFromISR(xPortStatus[1].xRxedChars, &cChar, pdFALSE)) {
|
190 | taskYIELD();
|
191 | }
|
192 | }
|
193 |
|
194 | // Send character
|
195 | SIGNAL(SIG_UART0_DATA) {
|
196 | signed portCHAR cChar, cTaskWoken;
|
197 | if (xQueueReceiveFromISR(xPortStatus[0].xCharsForTx, &cChar, &cTaskWoken ) == pdTRUE) {
|
198 | // Send the next character queued for Tx.
|
199 | UDR0 = cChar;
|
200 | } else {
|
201 | // Queue empty, nothing to send, stop interrupt
|
202 | vInterruptOff(0);
|
203 | }
|
204 | }
|
205 | SIGNAL(SIG_UART1_DATA) {
|
206 | signed portCHAR cChar, cTaskWoken;
|
207 | if (xQueueReceiveFromISR(xPortStatus[1].xCharsForTx, &cChar, &cTaskWoken ) == pdTRUE) {
|
208 | // Send the next character queued for Tx.
|
209 | UDR1 = cChar;
|
210 | } else {
|
211 | // Queue empty, nothing to send, stop interrupt
|
212 | vInterruptOff(1);
|
213 | }
|
214 | }
|