serial.c


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
}