aboutsummaryrefslogtreecommitdiff
path: root/lib/usart_enhanced.c
blob: 9ca9b479b5d33d808d312d1812a8a952d8f1e0d5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/* This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
/** library for enhanced USART communication (code)
 *  @file usart_enhanced.c
 *  @author King Kévin <kingkevin@cuvoodoo.info>
 *  @date 2018
 *  @details the USART peripherals only support 8 or 9-bit word and even or odd parity (included in the data bits). The library adds support for 5 to 8-bit words, none/even/odd/mark/space parity (on top of the data bits)
 *  @note since parity is handled in software, the parity error (PE) flag is unused and should be replaced by the value return by usart_enhanced_parity_error
 *  @remark 9-bit raw communication is not supported since this is not common and can be done without this library
 */
/* standard libraries */
#include <stdint.h> // standard integer types

/* STM32 (including CM3) libraries */
#include <libopencm3/stm32/usart.h> // USART utilities

/* own libraries */
#include "usart_enhanced.h" // utilities for USART enhancements

/** number of available USART peripherals */
#define USART_PERIPHERALS_NB 3
/** configured enhanced USART word size */
static uint8_t usart_enhanced_databits[USART_PERIPHERALS_NB];
/** configured enhanced USART parity */
static enum usart_enhanced_parity_t usart_enhanced_parity[USART_PERIPHERALS_NB];
/** last enhanced USART parity error status */
static bool usart_enhanced_parity_errors[USART_PERIPHERALS_NB];

const bool usart_enhanced_even_parity_lut[256] = { true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, };

/** get index of corresponding enhanced USART configurations
 *  @param[in] usart USART peripheral base address
 *  @return index used for the individual USART configurations
 *  @note the returned value is valid only is less than USART_PERIPHERALS_NB
 */
static uint8_t usart_enhanced_id(uint32_t usart)
{
	uint8_t to_return = USART_PERIPHERALS_NB;
	switch (usart) {
	case USART1:
		to_return = 0;
		break;
	case USART2:
		to_return = 1;
		break;
	case USART3:
		to_return = 2;
		break;
	default:
		to_return = USART_PERIPHERALS_NB;
		break;
	}
	return to_return;
}

bool usart_enhanced_config(uint32_t usart, uint8_t databits, enum usart_enhanced_parity_t parity)
{
	/* sanity check */
	uint8_t id = usart_enhanced_id(usart);
	if (id>=USART_PERIPHERALS_NB) {
		return false;
	}
	if (databits<5 || databits>8) {
		return false;
	}
	if (parity>USART_ENHANCED_PARITY_SPACE) {
		return false;
	}

	// save configuration for later use
	usart_enhanced_databits[id] = databits;
	usart_enhanced_parity[id] = parity;
	// configure USART peripheral
	if (8 == databits && USART_ENHANCED_PARITY_NONE != parity) { // the parity bit is additional to the data bits
		usart_set_databits(usart, 9);
	} else {
		usart_set_databits(usart, 8);
	}
	usart_set_parity(usart, USART_PARITY_NONE); // set no parity since we will take care of it ourselves
	// we could also lower the number of stop bits when less than 8 bits are used, for higher throughput, but this is not a good idea since most UART transceiver parse 8 bits even is less is used
	return true;
}

void usart_enhanced_send(uint32_t usart, uint8_t data)
{
	/* sanity check */
	uint8_t id = usart_enhanced_id(usart);
	if (id >= USART_PERIPHERALS_NB) {
		return;
	}

	data &= ~(0xff << usart_enhanced_databits[id]); // only keep the data bits
	uint16_t output = data; // put value in output buffer
	switch (usart_enhanced_parity[id]) {
	case USART_ENHANCED_PARITY_NONE: // a mark is also decoded as idle/stop
	case USART_ENHANCED_PARITY_MARK:
		output |= (1 << usart_enhanced_databits[id]); // add idle state
		break;
	case USART_ENHANCED_PARITY_EVEN:
		if (!usart_enhanced_even_parity_lut[data]) {
			output |= (1 << usart_enhanced_databits[id]);
		}
		// no need to clear a bit if the parity is even
		break;
	case USART_ENHANCED_PARITY_ODD:
		if (usart_enhanced_even_parity_lut[data]) {
			output |= (1 << usart_enhanced_databits[id]);
		}
		// no need to clear a bit if the parity is odd
		break;
	case USART_ENHANCED_PARITY_SPACE:
		// no need to clear the bit
		break;
	}
	output |= (0xffff << (usart_enhanced_databits[id] + 1)); // set additional bits to idle (high)
	usart_send(usart, output); // transmit character
}

uint8_t usart_enhanced_recv(uint32_t usart)
{
	/* sanity check */
	uint8_t id = usart_enhanced_id(usart);
	if (id >= USART_PERIPHERALS_NB) {
		return 0xff;
	}

	uint16_t input = usart_recv(usart); // read received character (also clears the error flags)
	uint8_t data = input & ~(0xffff << usart_enhanced_databits[id]); // only keep the data bits
	// check the parity
	uint16_t parity = input & (1 << usart_enhanced_databits[id]); // only keep the parity bit
	usart_enhanced_parity_errors[id] = false;
	switch (usart_enhanced_parity[id]) {
	case USART_ENHANCED_PARITY_NONE:
		usart_enhanced_parity_errors[id] = false;
		break;
	case USART_ENHANCED_PARITY_EVEN:
		if (parity) {
			usart_enhanced_parity_errors[id] = !usart_enhanced_even_parity_lut[data];
		} else {
			usart_enhanced_parity_errors[id] = usart_enhanced_even_parity_lut[data];
		}
		break;
	case USART_ENHANCED_PARITY_ODD:
		if (parity) {
			usart_enhanced_parity_errors[id] = usart_enhanced_even_parity_lut[data];
		} else {
			usart_enhanced_parity_errors[id] = !usart_enhanced_even_parity_lut[data];
		}
		break;
	case USART_ENHANCED_PARITY_MARK:
		usart_enhanced_parity_errors[id] = !parity;
		break;
	case USART_ENHANCED_PARITY_SPACE:
		usart_enhanced_parity_errors[id] = parity;
		break;
	}
	return data;
}

bool usart_enhanced_parity_error(uint32_t usart)
{
	/* sanity check */
	uint8_t id = usart_enhanced_id(usart);
	if (id >= USART_PERIPHERALS_NB) {
		return false;
	}
	return usart_enhanced_parity_errors[id];
}