aboutsummaryrefslogtreecommitdiff
path: root/lib/uart_soft.c
blob: 105d811e4db37876ed7656fc84bd16b87902acb7 (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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
/* 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 to control up to 4 independent receive and transmit software UART ports (code)
 *  @file uart_soft.c
 *  @author King Kévin <kingkevin@cuvoodoo.info>
 *  @date 2016
 *  @note peripherals used: GPIO @ref uart_soft_gpio, timer @ref uart_soft_timer
 */

/* standard libraries */
#include <stdint.h> // standard integer types
#include <stdlib.h> // general utilities

/* STM32 (including CM3) libraries */
#include <libopencm3/stm32/rcc.h> // real-time control clock library
#include <libopencm3/stm32/gpio.h> // general purpose input output library
#include <libopencm3/stm32/timer.h> // timer library
#include <libopencm3/cm3/nvic.h> // interrupt handler
#include <libopencm3/stm32/exti.h> // external interrupt defines
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities

#include "uart_soft.h" // software UART library API
#include "global.h" // common methods

/** @defgroup uart_soft_gpio GPIO used for the software 4 UART ports
 *  @note comment if unused
 *  @warning only one port must be used per line (pin number)
 *  @{
 */
#define UART_SOFT_RX_PORT0 B /**< port for receive signal for UART port 0 */
#define UART_SOFT_RX_PIN0 9 /**< pin for receive signal for UART port 0 */
//#define UART_SOFT_RX_PORT1 A /**< port for receive signal for UART port 0 */
//#define UART_SOFT_RX_PIN1 0 /**< pin for receive signal for UART port 0 */
//#define UART_SOFT_RX_PORT2 A /**< port for receive signal for UART port 0 */
//#define UART_SOFT_RX_PIN2 0 /**< pin for receive signal for UART port 0 */
//#define UART_SOFT_RX_PORT3 A /**< port for receive signal for UART port 0 */
//#define UART_SOFT_RX_PIN3 0 /**< pin for receive signal for UART port 0 */
#define UART_SOFT_TX_PORT0 B /**< port for transmit signal for UART port 0 */
#define UART_SOFT_TX_PIN0 8 /**< pin for transmit signal for UART port 0 */
//#define UART_SOFT_TX_PORT1 A /**< port for transmit signal for UART port 0 */
//#define UART_SOFT_TX_PIN1 0 /**< pin for transmit signal for UART port 0 */
//#define UART_SOFT_TX_PORT2 A /**< port for transmit signal for UART port 0 */
//#define UART_SOFT_TX_PIN2 0 /**< pin for transmit signal for UART port 0 */
//#define UART_SOFT_TX_PORT3 A /**< port for transmit signal for UART port 0 */
//#define UART_SOFT_TX_PIN3 0 /**< pin for transmit signal for UART port 0 */
/** @} */

/** buffer size for receive and transmit buffers */
#define UART_SOFT_BUFFER 128
/** UART receive state definition */
struct soft_uart_rx_state {
	uint32_t port; /**< UART receive port */
	uint16_t pin; /**< UART receive pin */
	uint32_t rcc; /**< UART receive port peripheral clock */
	uint32_t exti; /**< UART receive external interrupt */
	uint32_t irq; /**< UART receive interrupt request */
	uint32_t baudrate; /**< UART receive baud rate */
	volatile uint16_t state; /**< GPIO state for receive pin */
	volatile uint8_t bit; /**< next UART frame bit to receive */
	volatile uint8_t byte; /**< byte being received */
	volatile uint8_t buffer[UART_SOFT_BUFFER]; /**< receive buffer */
	volatile uint8_t buffer_i; /**< index of current data to be read out */
	volatile uint8_t buffer_used; /**< how much data is available */
	volatile bool lock; /**< put lock when changing buffer_i or buffer_used */
	volatile uint8_t buffer_byte; /**< to temporary store byte while locked */
	volatile bool buffer_byte_used; /**< signal a byte has been stored in temporary buffer */

};
/** UART transmit state definition */
struct soft_uart_tx_state {
	uint32_t port; /**< UART receive port */
	uint16_t pin; /**< UART receive pin */
	uint32_t rcc; /**< UART receive port peripheral clock */
	uint32_t baudrate; /**< UART receive baud rate */
	volatile uint8_t bit; /**< next UART frame bit to transmit */
	volatile uint8_t byte; /**< byte being transmitted */
	volatile uint8_t buffer[UART_SOFT_BUFFER]; /**< receive buffer */
	volatile uint8_t buffer_i; /**< index of current data to be read out */
	volatile uint8_t buffer_used; /**< how much data is available */
	volatile bool transmit; /**< flag to know it transmission is ongoing */
};

static struct soft_uart_rx_state* uart_soft_rx_states[4] = {NULL}; /**< states of UART receive ports (up to 4) */
static struct soft_uart_tx_state* uart_soft_tx_states[4] = {NULL}; /**< states of UART transmit ports (up to 4) */

volatile bool uart_soft_received[4] = {false, false, false, false};

/** @defgroup uart_soft_timer timer used to sample UART signals
 *  @{
 */
#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0)) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1)) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2)) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN0))
	#define UART_SOFT_RX_TIMER 3 /**< timer peripheral for receive signals */
#endif
#if (defined(UART_SOFT_TX_PORT0) && defined(UART_SOFT_TX_PIN0)) || (defined(UART_SOFT_TX_PORT1) && defined(UART_SOFT_TX_PIN1)) || (defined(UART_SOFT_TX_PORT2) && defined(UART_SOFT_TX_PIN2)) || (defined(UART_SOFT_TX_PORT3) && defined(UART_SOFT_TX_PIN0))
	#define UART_SOFT_TX_TIMER 4 /**< timer peripheral for transmit signals */
#endif
/** @} */

static const uint32_t timer_flags[4] = {TIM_SR_CC1IF,TIM_SR_CC2IF,TIM_SR_CC3IF,TIM_SR_CC4IF}; /**< the interrupt flags for the compare units */
static const uint32_t timer_interrupt[4] = {TIM_DIER_CC1IE,TIM_DIER_CC2IE,TIM_DIER_CC3IE,TIM_DIER_CC4IE}; /**< the interrupt enable for the compare units */
static const enum tim_oc_id timer_oc[4] = {TIM_OC1,TIM_OC2,TIM_OC3,TIM_OC4}; /**< the output compares for the compare units */

bool uart_soft_setup(uint32_t *rx_baudrates, uint32_t *tx_baudrates)
{
	(void)rx_baudrates; // ensure compile does no complain even if no receive port is used
	(void)tx_baudrates; // ensure compile does no complain even if no transmit port is used

	// save UART receive definition
#if defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0)
	uart_soft_rx_states[0] = calloc(1,sizeof(struct soft_uart_rx_state)); // create state definition
	uart_soft_rx_states[0]->port = GPIO(UART_SOFT_RX_PORT0); // save receive port
	uart_soft_rx_states[0]->pin = GPIO(UART_SOFT_RX_PIN0); // save receive pin
	uart_soft_rx_states[0]->rcc = RCC_GPIO(UART_SOFT_RX_PORT0); // save receive port peripheral clock
	uart_soft_rx_states[0]->exti = EXTI(UART_SOFT_RX_PIN0); // save receive external interrupt
	uart_soft_rx_states[0]->irq = NVIC_EXTI_IRQ(UART_SOFT_RX_PIN0); // save receive interrupt request
#endif

	// setup UART receive GPIO
	for (uint8_t rx=0; rx<4; rx++) {
		if (!uart_soft_rx_states[rx]) { // verify is receive UART is defined
			continue; // skip configuration if not defined
		}
		if (!rx_baudrates || rx_baudrates[rx]==0) { // verify if receive baud rate has been defined
			return false;
		}
		uart_soft_rx_states[rx]->baudrate = rx_baudrates[rx]; // save baud rate
		rcc_periph_clock_enable(uart_soft_rx_states[rx]->rcc); // enable clock for GPIO peripheral
		gpio_set_mode(uart_soft_rx_states[rx]->port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, uart_soft_rx_states[rx]->pin); // setup GPIO pin UART receive
		gpio_set(uart_soft_rx_states[rx]->port, uart_soft_rx_states[rx]->pin); // pull up to avoid noise when not connected
		rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt
		exti_select_source(uart_soft_rx_states[rx]->exti, uart_soft_rx_states[rx]->port); // mask external interrupt of this pin only for this port	
		exti_enable_request(uart_soft_rx_states[rx]->exti); // enable external interrupt
		exti_set_trigger(uart_soft_rx_states[rx]->exti, EXTI_TRIGGER_BOTH); // trigger when button is pressed
		nvic_enable_irq(uart_soft_rx_states[rx]->irq); // enable interrupt
		uart_soft_rx_states[rx]->state = gpio_get(uart_soft_rx_states[rx]->port, uart_soft_rx_states[rx]->pin); // save state of GPIO
		uart_soft_rx_states[rx]->bit = 0; // reset bits received
	}

	// save UART transmit definition
#if defined(UART_SOFT_TX_PORT0) && defined(UART_SOFT_TX_PIN0)	
	uart_soft_tx_states[0] = calloc(1,sizeof(struct soft_uart_tx_state)); // create state definition
	uart_soft_tx_states[0]->port = GPIO(UART_SOFT_TX_PORT0); // save receive port
	uart_soft_tx_states[0]->pin = GPIO(UART_SOFT_TX_PIN0); // save receive pin
	uart_soft_tx_states[0]->rcc = RCC_GPIO(UART_SOFT_TX_PORT0); // save receive port peripheral clock
#endif
	
	// setup UART transmit GPIO
	for (uint8_t tx=0; tx<4; tx++) {
		if (!uart_soft_tx_states[tx]) { // verify is transmit UART is defined
			continue; // skip configuration if not defined
		}
		if (!tx_baudrates || tx_baudrates[tx]==0) { // verify if transmit baud rate has been defined
			return false;
		}
		uart_soft_tx_states[tx]->baudrate = tx_baudrates[tx]; // save baud rate
		rcc_periph_clock_enable(uart_soft_tx_states[tx]->rcc); // enable clock for GPIO peripheral
		gpio_set_mode(uart_soft_tx_states[tx]->port, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, uart_soft_tx_states[tx]->pin); // setup GPIO UART transmit pin
		gpio_set(uart_soft_tx_states[tx]->port, uart_soft_tx_states[tx]->pin); // idle high
	}

	// setup timer
#if defined(UART_SOFT_RX_TIMER)
	rcc_periph_clock_enable(RCC_TIM(UART_SOFT_RX_TIMER)); // enable clock for timer peripheral
	timer_reset(TIM(UART_SOFT_RX_TIMER)); // reset timer state
	timer_set_mode(TIM(UART_SOFT_RX_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock, edge alignment (simple count), and count up
	timer_set_prescaler(TIM(UART_SOFT_RX_TIMER), 0); // prescaler to be able to sample 2400-115200 bps (72MHz/2^16=1099<2400bps)
	nvic_enable_irq(NVIC_TIM_IRQ(UART_SOFT_RX_TIMER)); // allow interrupt for timer
	timer_enable_counter(TIM(UART_SOFT_RX_TIMER)); // start timer to generate interrupts for the receive pins
#endif
#if defined(UART_SOFT_TX_TIMER)
	rcc_periph_clock_enable(RCC_TIM(UART_SOFT_TX_TIMER)); // enable clock for timer peripheral
	timer_reset(TIM(UART_SOFT_TX_TIMER)); // reset timer state
	timer_set_mode(TIM(UART_SOFT_TX_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock, edge alignment (simple count), and count up
	timer_set_prescaler(TIM(UART_SOFT_TX_TIMER), 0); // prescaler to be able to output 2400-115200 bps (72MHz/2^16=1099<2400bps)
	nvic_enable_irq(NVIC_TIM_IRQ(UART_SOFT_TX_TIMER)); // allow interrupt for timer
	timer_enable_counter(TIM(UART_SOFT_TX_TIMER)); // start timer to generate interrupts for the transmit pins
#endif

	return true; // setup completed
}

#if defined(UART_SOFT_RX_TIMER)
uint8_t uart_soft_getbyte(uint8_t uart)
{
	if (uart>=4 || !uart_soft_rx_states[uart]) { // ensure receive UART port is defined
		return 0; // return
	}
	while (!uart_soft_rx_states[uart]->buffer_used) { // idle until data is available
		__WFI(); // sleep until interrupt
	}
	uart_soft_rx_states[uart]->lock = true; // set lock
	uint8_t to_return = uart_soft_rx_states[uart]->buffer[uart_soft_rx_states[uart]->buffer_i]; // get the next available character
	uart_soft_rx_states[uart]->buffer_i = (uart_soft_rx_states[uart]->buffer_i+1)%LENGTH(uart_soft_rx_states[uart]->buffer); // update used buffer
	uart_soft_rx_states[uart]->buffer_used--; // update used buffer
	uart_soft_rx_states[uart]->lock = false; // free lock
	if (uart_soft_rx_states[uart]->buffer_byte_used) { // temporary byte has been stored
		uart_soft_rx_states[uart]->buffer[(uart_soft_rx_states[uart]->buffer_i+uart_soft_rx_states[uart]->buffer_used)%LENGTH(uart_soft_rx_states[uart]->buffer)] = uart_soft_rx_states[uart]->buffer_byte; // put byte in buffer
		uart_soft_rx_states[uart]->buffer_used++; // update used buffer
		uart_soft_rx_states[uart]->buffer_byte_used = false; // buffer byte is now in buffer
	}
	uart_soft_received[uart] = (uart_soft_rx_states[uart]->buffer_used!=0); // notify user if data is available
	uart_soft_rx_states[uart]->lock = false; // free lock
	return to_return;
}

/** timer interrupt service routine to generate UART transmit signals */
void TIM_ISR(UART_SOFT_RX_TIMER)(void)
{
	for (uint8_t rx=0; rx<4; rx++) {
		if (timer_interrupt_source(TIM(UART_SOFT_RX_TIMER),timer_flags[rx])) { // got a match on compare for receive pin
			timer_clear_flag(TIM(UART_SOFT_RX_TIMER),timer_flags[rx]); // clear flag
			if (!uart_soft_rx_states[rx]) { // verify if RX exists
				continue; // skip if receive port is not defined it
			}
			uart_soft_rx_states[rx]->byte += ((gpio_get(uart_soft_rx_states[rx]->port, uart_soft_rx_states[rx]->pin)==0 ? 0 : 1)<<(uart_soft_rx_states[rx]->bit-1)); // save bit value
			if (uart_soft_rx_states[rx]->bit<8) { // not the last bit received
				timer_set_oc_value(TIM(UART_SOFT_RX_TIMER),timer_oc[rx],timer_get_counter(TIM(UART_SOFT_RX_TIMER))+rcc_ahb_frequency/uart_soft_rx_states[rx]->baudrate); // set timer to next bit
				uart_soft_rx_states[rx]->bit++; // wait for next bit
			} else { // last bit received
				if (uart_soft_rx_states[rx]->lock) { // someone is already reading data
					uart_soft_rx_states[rx]->buffer_byte = uart_soft_rx_states[rx]->byte; // save byte
					uart_soft_rx_states[rx]->buffer_byte_used = true; // notify reader there is a temporary byte
				} else { // buffer can be updated
					if (uart_soft_rx_states[rx]->buffer_used>=LENGTH(uart_soft_rx_states[rx]->buffer)) { // buffer is full
						uart_soft_rx_states[rx]->buffer_i = (uart_soft_rx_states[rx]->buffer_i+1)%LENGTH(uart_soft_rx_states[rx]->buffer); // drop oldest byte
						uart_soft_rx_states[rx]->buffer_used--; // update buffer usage
					}
					uart_soft_rx_states[rx]->buffer[(uart_soft_rx_states[rx]->buffer_i+uart_soft_rx_states[rx]->buffer_used)%LENGTH(uart_soft_rx_states[rx]->buffer)] = uart_soft_rx_states[rx]->byte; // put byte in buffer
					uart_soft_rx_states[rx]->buffer_used++; // update used buffer
					uart_soft_received[rx] = true; // notify user data is available
				}
				timer_disable_irq(TIM(UART_SOFT_RX_TIMER),timer_interrupt[rx]); // stop_interrupting
				uart_soft_rx_states[rx]->bit = 0; // next bit should be first bit of next byte
			}
		}
	}
}
#endif

#if defined(UART_SOFT_TX_TIMER)
void uart_soft_flush(uint8_t uart)
{
	if (uart>=4 || !uart_soft_tx_states[uart]) { // ensure transmit UART port is defined
		return; // return
	}
	while (uart_soft_tx_states[uart]->buffer_used) { // idle until buffer is empty
		__WFI(); // sleep until interrupt
	}
	while (uart_soft_tx_states[uart]->transmit) { // idle until transmission is complete
		__WFI(); // sleep until interrupt
	}
}

/** start transmitting a byte from the buffer
 *  @param[in] uart UART port used for transmission
 */
static void uart_soft_transmit(uint8_t uart) {
	if (uart>=4 || !uart_soft_tx_states[uart]) { // ensure transmit UART port is defined
		return; // UART transmit port not defined
	}
	if (uart_soft_tx_states[uart]->transmit) { // already transmitting
		return; // transmission is already ongoing
	}
	if (!uart_soft_tx_states[uart]->buffer_used) { // no buffered data to transmit
		return; // nothing to transmit
	}
	uart_soft_tx_states[uart]->byte = uart_soft_tx_states[uart]->buffer[uart_soft_tx_states[uart]->buffer_i]; // get byte
	uart_soft_tx_states[uart]->buffer_i = (uart_soft_tx_states[uart]->buffer_i+1)%LENGTH(uart_soft_tx_states[uart]->buffer); // update index
	uart_soft_tx_states[uart]->buffer_used--; // update used buffer
	uart_soft_tx_states[uart]->bit = 0; // LSb is transmitted first
	uart_soft_tx_states[uart]->transmit = true; // start transmission
	gpio_clear(uart_soft_tx_states[uart]->port, uart_soft_tx_states[uart]->pin); // output start bit
	timer_set_oc_value(TIM(UART_SOFT_TX_TIMER), timer_oc[uart], timer_get_counter(TIM(UART_SOFT_TX_TIMER))+(rcc_ahb_frequency/uart_soft_tx_states[uart]->baudrate)); // set timer to output UART frame 1 (data bit 0) in 1 bit
	timer_clear_flag(TIM(UART_SOFT_TX_TIMER), timer_flags[uart]); // clear flag before enabling interrupt
	timer_enable_irq(TIM(UART_SOFT_TX_TIMER), timer_interrupt[uart]);// enable timer IRQ for TX for this UART
}

void uart_soft_putbyte_nonblocking(uint8_t uart, uint8_t byte)
{
	if (uart>=4 || !uart_soft_tx_states[uart]) { // ensure transmit UART port is defined
		return; // return
	}	
	while (uart_soft_tx_states[uart]->buffer_used>=LENGTH(uart_soft_tx_states[uart]->buffer)) { // idle until there is place in the buffer
		__WFI(); // sleep until something happened
	}
	uart_soft_tx_states[uart]->buffer[(uart_soft_tx_states[uart]->buffer_i+uart_soft_tx_states[uart]->buffer_used)%LENGTH(uart_soft_tx_states[uart]->buffer)] = byte; // save byte to be transmitted
	uart_soft_tx_states[uart]->buffer_used++; // update used buffer
	uart_soft_transmit(uart); // start transmission
}

void uart_soft_putbyte_blocking(uint8_t uart, uint8_t byte)
{
	uart_soft_putbyte_nonblocking(uart, byte); // put byte in queue
	uart_soft_flush(uart); // wait for all byte to be transmitted
}

/** timer interrupt service routine to sample UART receive signals */
void TIM_ISR(UART_SOFT_TX_TIMER)(void)
{
	for (uint8_t tx=0; tx<4; tx++) {
		if (timer_interrupt_source(TIM(UART_SOFT_TX_TIMER),timer_flags[tx])) { // got a match on compare for transmit pin
			timer_clear_flag(TIM(UART_SOFT_TX_TIMER),timer_flags[tx]); // clear flag
			if (!uart_soft_tx_states[tx]) { // verify if transmit is defined
				continue; // skip if transmit port is not defined it
			}
			if (uart_soft_tx_states[tx]->bit<8) { // there is a data bit to transmit
				if ((uart_soft_tx_states[tx]->byte>>uart_soft_tx_states[tx]->bit)&0x01) { // bit to transmit is a 1
					gpio_set(uart_soft_tx_states[tx]->port, uart_soft_tx_states[tx]->pin); // set output to high
				} else { // bit to transmit is a 0
					gpio_clear(uart_soft_tx_states[tx]->port, uart_soft_tx_states[tx]->pin); // set output to low
				}
				timer_set_oc_value(TIM(UART_SOFT_TX_TIMER), timer_oc[tx], timer_get_counter(TIM(UART_SOFT_TX_TIMER))+(rcc_ahb_frequency/uart_soft_tx_states[tx]->baudrate)); // wait for the next frame bit
				uart_soft_tx_states[tx]->bit++; // go to next bit
			} else if (uart_soft_tx_states[tx]->bit==8) { // transmit stop bit
				gpio_set(uart_soft_tx_states[tx]->port, uart_soft_tx_states[tx]->pin); // go idle high
				timer_set_oc_value(TIM(UART_SOFT_TX_TIMER), timer_oc[tx], timer_get_counter(TIM(UART_SOFT_TX_TIMER))+(rcc_ahb_frequency/uart_soft_tx_states[tx]->baudrate)); // wait for 1 stop bit
				uart_soft_tx_states[tx]->bit++; // go to next bit
			} else { // UART frame is complete
				timer_disable_irq(TIM(UART_SOFT_TX_TIMER), timer_interrupt[tx]);// enable timer IRQ for TX for this UART
				uart_soft_tx_states[tx]->transmit = false; // transmission finished
				uart_soft_transmit(tx); // start next transmission (if there is)
			}
		} // compare match
	} // go through UARTs
}
#endif

/** central function handling receive signal activity */
static void uart_soft_receive_activity(void)
{
	for (uint8_t rx=0; rx<4; rx++) {
		if (!uart_soft_rx_states[rx]) { // verify if receive port is not configured
			continue; // skip if receive port is not defined it
		}
		if (uart_soft_rx_states[rx]->state!=gpio_get(uart_soft_rx_states[rx]->port, uart_soft_rx_states[rx]->pin)) { // only do something if state changed
			uart_soft_rx_states[rx]->state = gpio_get(uart_soft_rx_states[rx]->port, uart_soft_rx_states[rx]->pin); // save new state
			if (uart_soft_rx_states[rx]->bit==0) { // start bit edge detected
				if (uart_soft_rx_states[rx]->state==0) { // start bit has to be low
					timer_set_oc_value(TIM(UART_SOFT_RX_TIMER), timer_oc[rx], timer_get_counter(TIM(UART_SOFT_RX_TIMER))+(rcc_ahb_frequency/uart_soft_rx_states[rx]->baudrate)*1.5); // set timer to sample data bit 0 in 1.5 bits
					timer_clear_flag(TIM(UART_SOFT_RX_TIMER), timer_flags[rx]); // clear flag before enabling interrupt
					timer_enable_irq(TIM(UART_SOFT_RX_TIMER), timer_interrupt[rx]);// enable timer IRQ for RX for this UART
					uart_soft_rx_states[rx]->byte = 0; // reset byte value
					uart_soft_rx_states[rx]->bit++; // wait for first bit
				}
			} else { // data bit detected
				timer_set_oc_value(TIM(UART_SOFT_RX_TIMER), timer_oc[rx], timer_get_counter(TIM(UART_SOFT_RX_TIMER))+(rcc_ahb_frequency/uart_soft_rx_states[rx]->baudrate)/2); // resync timer to half a bit (good for drifting transmission, bad if the line is noisy)
			}
		}
	}
}

/** GPIO interrupt service routine to detect UART receive activity */
#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && UART_SOFT_RX_PIN0==0) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && UART_SOFT_RX_PIN1==0) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && UART_SOFT_RX_PIN2==0) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && UART_SOFT_RX_PIN3==0)
void exti0_isr(void)
{
	exti_reset_request(EXTI0); // clear interrupt flag for pin triggers this ISR (pin state will be checked independently)
	uart_soft_receive_activity(); // check which GPIO changed
}
#endif
#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && UART_SOFT_RX_PIN0==1) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && UART_SOFT_RX_PIN1==1) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && UART_SOFT_RX_PIN2==1) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && UART_SOFT_RX_PIN3==1)
void exti1_isr(void)
{
	exti_reset_request(EXTI1); // clear interrupt flag for pin triggers this ISR (pin state will be checked independently)
	uart_soft_receive_activity(); // check which GPIO changed
}
#endif
#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && UART_SOFT_RX_PIN0==2) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && UART_SOFT_RX_PIN1==2) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && UART_SOFT_RX_PIN2==2) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && UART_SOFT_RX_PIN3==2)
void exti2_isr(void)
{
	exti_reset_request(EXTI2); // clear interrupt flag for pin triggers this ISR (pin state will be checked independently)
	uart_soft_receive_activity(); // check which GPIO changed
}
#endif
#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && UART_SOFT_RX_PIN0==3) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && UART_SOFT_RX_PIN1==3) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && UART_SOFT_RX_PIN2==3) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && UART_SOFT_RX_PIN3==3)
void exti3_isr(void)
{
	exti_reset_request(EXTI3); // clear interrupt flag for pin triggers this ISR (pin state will be checked independently)
	uart_soft_receive_activity(); // check which GPIO changed
}
#endif
#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && UART_SOFT_RX_PIN0==4) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && UART_SOFT_RX_PIN1==4) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && UART_SOFT_RX_PIN2==4) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && UART_SOFT_RX_PIN3==4)
void exti4_isr(void)
{
	exti_reset_request(EXTI4); // clear interrupt flag for pin triggers this ISR (pin state will be checked independently)
	uart_soft_receive_activity(); // check which GPIO changed
}
#endif
#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && (UART_SOFT_RX_PIN0==5 || UART_SOFT_RX_PIN0==6 || UART_SOFT_RX_PIN0==7 || UART_SOFT_RX_PIN0==8 || UART_SOFT_RX_PIN0==9)) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && (UART_SOFT_RX_PIN1==5 || UART_SOFT_RX_PIN1==6 || UART_SOFT_RX_PIN1==7 || UART_SOFT_RX_PIN1==8 || UART_SOFT_RX_PIN1==9)) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && (UART_SOFT_RX_PIN2==5 || UART_SOFT_RX_PIN2==6 || UART_SOFT_RX_PIN2==7 || UART_SOFT_RX_PIN2==8 || UART_SOFT_RX_PIN2==9)) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && (UART_SOFT_RX_PIN3==5 || UART_SOFT_RX_PIN3==6 || UART_SOFT_RX_PIN3==7 || UART_SOFT_RX_PIN3==8 || UART_SOFT_RX_PIN3==9))
void exti9_5_isr(void)
{
	exti_reset_request(EXTI5|EXTI6|EXTI7|EXTI8|EXTI9); // clear interrupt flag for pin triggers this ISR (pin state will be checked independently)
	uart_soft_receive_activity(); // check which GPIO changed
}
#endif
#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && (UART_SOFT_RX_PIN0==10 || UART_SOFT_RX_PIN0==11 || UART_SOFT_RX_PIN0==12 || UART_SOFT_RX_PIN0==13 || UART_SOFT_RX_PIN0==14 || UART_SOFT_RX_PIN0==15)) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && (UART_SOFT_RX_PIN1==10 || UART_SOFT_RX_PIN1==11 || UART_SOFT_RX_PIN1==12 || UART_SOFT_RX_PIN1==13 || UART_SOFT_RX_PIN1==14 || UART_SOFT_RX_PIN1==15)) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && (UART_SOFT_RX_PIN2==10 || UART_SOFT_RX_PIN2==11 || UART_SOFT_RX_PIN2==12 || UART_SOFT_RX_PIN2==13 || UART_SOFT_RX_PIN2==14 || UART_SOFT_RX_PIN2==15)) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && (UART_SOFT_RX_PIN3==10 || UART_SOFT_RX_PIN3==11 || UART_SOFT_RX_PIN3==12 || UART_SOFT_RX_PIN3==13 || UART_SOFT_RX_PIN3==14 || UART_SOFT_RX_PIN3==15))
void exti15_10_isr(void)
{
	exti_reset_request(EXTI10|EXTI11|EXTI12|EXTI13|EXTI14|EXTI15); // clear interrupt flag for pin triggers this ISR (pin state will be checked independently)
	uart_soft_receive_activity(); // check which GPIO changed
}
#endif