SP8EBC-ParaTNC/system/src/drivers/serial.c

436 wiersze
13 KiB
C
Czysty Wina Historia

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

#include "drivers/serial.h"
#include "drivers/gpio_conf.h"
#define PORT USART1
#include <stm32f10x.h>
#include <stm32f10x_usart.h>
#include "station_config.h"
#include "diag/Trace.h"
#include "main.h" // global_time is here
#include <string.h>
int srlBRRegValue = 0x09C4 ; // dla symulacji ---- baudrate 9600bps
//int SrlBRRegValue = 0x0209; // dla realnego uk<75><6B>du
#ifdef SEPARATE_TX_BUFF
uint8_t srl_tx_buffer[TX_BUFFER_LN] = {'\0'}; // dane do wys<79>ania do zdalnego urz<72>dzenia
#endif
#ifdef SEPARATE_RX_BUFF
uint8_t srl_rx_buffer[RX_BUFFER_LN] = {'\0'}; // dane odebrane od zdalnego urz<72>dzenia
#endif
uint8_t *srl_tx_buf_pointer;
uint8_t *srl_rx_buf_pointer;
uint16_t srl_rx_buf_ln = 0;
uint16_t srl_tx_buf_ln = 0;
uint16_t srl_rx_bytes_counter = 0;
uint16_t srl_tx_bytes_counter = 0;
uint16_t srl_rx_bytes_req = 0;
uint16_t srl_tx_bytes_req = 0;
uint8_t srl_triggered_start = 0;
uint8_t srl_triggered_stop = 0;
uint8_t srl_start_trigger = 0x00; // znak oznaczaj<61>cy pocz<63>tek istotnych danych do odbebrania
uint8_t srl_stop_trigger = 0x00; // znak oznaczaj<61>cy koniec istotnych danych do odebrania
volatile uint8_t srl_garbage_storage;
uint8_t srl_rx_timeout_enable = 0;
uint8_t srl_rx_timeout_calc_started = 0;
uint32_t srl_rx_start_time = 0;
srlRxState srl_rx_state = SRL_RX_NOT_CONFIG;
srlTxState srl_tx_state = SRL_TX_NOT_CONFIG;
uint8_t srl_enable_echo = 0;
uint8_t srl_rx_lenght_param_addres = 0;
uint8_t srl_rx_lenght_param_modifier = 0;
void srl_init(void) {
#ifdef SEPARATE_TX_BUFF
srl_tx_buf_pointer = srl_tx_buffer;
srl_rx_buf_ln = RX_BUFFER_LN;
#endif
#ifdef SEPARATE_RX_BUFF
srl_rx_buf_pointer = srl_rx_buffer;
srl_tx_buf_ln = TX_BUFFER_LN;
#endif
Configure_GPIO(GPIOA,11,AFPP_OUTPUT_2MHZ); // CTS
Configure_GPIO(GPIOA,12,AFPP_OUTPUT_2MHZ); // RTS
Configure_GPIO(GPIOA,8,AFPP_OUTPUT_2MHZ); // CLK
Configure_GPIO(GPIOA,10,PUD_INPUT); // RX
Configure_GPIO(GPIOA,9,AFPP_OUTPUT_2MHZ); // TX
RCC->APB2ENR |= RCC_APB2ENR_USART1EN; // wġṗczanie zegara dla USART
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = _SERIAL_BAUDRATE;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx;
USART_Init(PORT, &USART_InitStructure);
NVIC_EnableIRQ( USART1_IRQn );
//NVIC_SetPriority(USART1_IRQn, 9); /// bylo 10
PORT->CR1 |= USART_CR1_UE;
// PORT->BRR |= srlBRRegValue; // ustawianie wartoœci preskalera do baudrate
//srlTXing = 0;
//srlIdle = 1;
PORT->SR &= (0xFFFFFFFF ^ USART_SR_TC);
// PORT->CR1 |= USART_CR1_IDLEIE; // zgġaszane kiedy przy odbiorze magistrala przejdzie w idle
// PORT->CR1 |= USART_CR1_RXNEIE; // przerwanie zgġoszone po odbiorze bajtu gdy bufor nie jest pusty
srl_rx_state = SRL_RX_IDLE;
srl_tx_state = SRL_TX_IDLE;
}
// this function shall be called in 10ms periods by some timer to check the timeout
// during receive. This method works differently depends on what receive mode was initiaded
//
// if start & stop characters are in use the timeout will be calculted from the time when
// start character was received and operation mode has been switched from SRL_WAITING_TO_RX
// to SRL_RXING
//
// if no start & stop character is used by software timeout is calculated from the time when
// first character was received after calling srl_receive_data
void srl_keep_timeout() {
if (srl_rx_state != SRL_RX_NOT_CONFIG && srl_rx_timeout_enable == 1) {
// checking if flag to check a timeout is raised
if (srl_rx_timeout_calc_started == 1) {
// check if timeout expired
if (master_time - srl_rx_start_time > SRL_RX_TIMEOUT_IN_MS) {
// disable the receiving part of UART, disable interrupt and switch to an error state
PORT->CR1 &= (0xFFFFFFFF ^ USART_CR1_RE);
PORT->CR1 &= (0xFFFFFFFF ^ USART_CR1_RXNEIE);
srl_rx_state = SRL_RX_ERROR;
}
}
}
else {
;
}
}
uint8_t srl_send_data(uint8_t* data, uint8_t mode, uint16_t leng, uint8_t internal_external) {
if (srl_tx_state == SRL_TXING)
return SRL_BUSY;
/* Wesja z dnia 04.09.2013
char* data - wskaznik na tablice z danymi do przeslania
char mode - tryb pracy ktory okresla czy ma wysylac okreslona liczbe znakow
czy wysylac wszystko do napotkania 0x00
short leng - ilosc znakow do wyslania istotna tylko gdy mode = 1
internal_external - ustawienie 0 spowoduje skopiowanie do wewnentrznego bufora i wysylanie z niego
jedynka spowoduje wysylanie bezposrednio z wewnetrznego
*/
int i;
// resetting counter
srl_tx_bytes_counter = 0;
// if an user want to send data using internal buffer
if (internal_external == 0) {
// if data at the input is too long to fit in the buffer
if (leng >= TX_BUFFER_LN)
return SRL_DATA_TOO_LONG;
// setting a pointer to transmit buffer to the internal buffer inside the driver
srl_tx_buf_pointer = srl_tx_buffer;
srl_tx_buf_ln = TX_BUFFER_LN;
// cleaning the buffer from previous content
memset(srl_tx_buf_pointer, 0x00, TX_BUFFER_LN);
// copying the data from provided pointer to internal buffer
if (mode == 0) {
// copying everything till the 0x00 is spoted or till the buffer border is reached
for (i = 0; (i < TX_BUFFER_LN && *(data+i) != '\0'); i++)
srl_tx_buf_pointer[i]=data[i];
srl_tx_bytes_req = i;
}
else if (mode == 1) {
// we don't need to check against buffer size because this was confirmed already
for (i = 0; i<=leng ; i++)
srl_tx_buf_pointer[i]=data[i];
srl_tx_bytes_req = leng;
}
}
else if (internal_external == 1) {
srl_tx_buf_pointer = data;
srl_tx_bytes_req = leng;
srl_tx_buf_ln = leng;
}
else return SRL_WRONG_BUFFER_PARAM;
// enabling transmitter
PORT->CR1 |= USART_CR1_TE;
PORT->SR &= (0xFFFFFFFF ^ USART_SR_TC);
PORT->DR = srl_tx_buf_pointer[0];
srl_tx_state = SRL_TXING;
PORT->CR1 |= USART_CR1_TXEIE; // przerwanie zg<7A>aszane kiedy rejsetr DR jest pusty
PORT->CR1 |= USART_CR1_TCIE; // przerwanie zg<7A>aszane po transmisji bajtu
// je<6A>eli rejestr DR jest nadal pusty
return SRL_OK;
}
/**
* This function assumes than
*/
uint8_t srl_start_tx(short leng) {
if (srl_tx_state == SRL_TXING)
return SRL_BUSY;
// if data at the input is too long to fit in the buffer
if (leng >= TX_BUFFER_LN)
return SRL_DATA_TOO_LONG;
srl_tx_bytes_req = leng;
srl_tx_bytes_counter = 0;
// setting a pointer to transmit buffer to the internal buffer inside the driver
srl_tx_buf_pointer = srl_tx_buffer;
PORT->CR1 |= USART_CR1_TE;
PORT->SR &= (0xFFFFFFFF ^ USART_SR_TC);
PORT->DR = srl_tx_buf_pointer[srl_tx_bytes_counter];
srl_tx_state = SRL_TXING;
PORT->CR1 |= USART_CR1_TXEIE; // przerwanie zg<7A>aszane kiedy rejsetr DR jest pusty
PORT->CR1 |= USART_CR1_TCIE; // przerwanie zg<7A>aszane po transmisji bajtu
// je<6A>eli rejestr DR jest nadal pusty
return SRL_OK;
}
uint8_t srl_receive_data(int num, char start, char stop, char echo, char len_addr, char len_modifier) {
if (srl_rx_state == SRL_RXING)
return SRL_BUSY;
//trace_printf("Serial:SrlReceiveData()\r\n");
if (num >= RX_BUFFER_LN)
return SRL_DATA_TOO_LONG;
memset(srl_rx_buf_pointer, 0x00, RX_BUFFER_LN);
// checking if user want
if (start != 0x00) {
srl_triggered_start = 1;
srl_start_trigger = start;
}
else {
srl_triggered_start = 0;
}
if (stop != 0x00) {
srl_triggered_stop = 1;
srl_stop_trigger = stop;
}
else {
srl_triggered_stop = 0;
}
if (srl_triggered_start == 1 || srl_triggered_stop == 1) {
if (num < 3)
return SRL_WRONG_PARAMS_COMBINATION;
srl_rx_state = SRL_WAITING_TO_RX;
}
srl_enable_echo = echo;
srl_rx_bytes_counter = 0;
srl_rx_bytes_req = num;
srl_rx_lenght_param_addres = len_addr;
srl_rx_lenght_param_modifier = len_modifier;
PORT->CR1 |= USART_CR1_RE; // uruchamianie odbiornika
PORT->CR1 |= USART_CR1_RXNEIE; // przerwanie od przepe<70>nionego bufora odbioru
// PORT->CR1 |= USART_CR1_IDLEIE; // przerwanie od bezczynno<6E>ci szyny RS przy odbiorze
// spowodowanej zako<6B>czeniem transmisji przez urz<72>dzenie
return SRL_OK;
}
void srl_irq_handler(void) {
// local variable for recalculating a stream length (how many bytes the driver should receives)
uint16_t len_temp = 0;
// set to one if there are conditions to stop receiving
uint8_t stop_rxing = 0;
// local variable to store
uint8_t value = 0;
// if overrun happened, a byte hadn't been transferred from DR before the next byte is received
if ((PORT->SR & USART_SR_ORE) == USART_SR_ORE) {
switch (srl_rx_state) {
case SRL_RXING:
srl_garbage_storage = (uint8_t)PORT->DR;
break;
default:
// if the UART driver is not receiving actually but hardware controler received any data
// it is required to read value of DR register to clear the interrupt
srl_garbage_storage = (uint8_t)PORT->DR;
break;
}
}
// if any data has been received by the UART controller
if ((PORT->SR & USART_SR_RXNE) == USART_SR_RXNE) {
switch (srl_rx_state) {
case SRL_RXING: {
// raise a flag to signalize that timeout shall be calulated from now.
srl_rx_timeout_calc_started = 1;
// if there is any data remaining to receive
if (srl_rx_bytes_counter < srl_rx_bytes_req) {
// storing received byte into buffer
srl_rx_buf_pointer[srl_rx_bytes_counter] = (uint8_t)PORT->DR;
// checking if this byte in stream holds the protocol information about
// how many bytes needs to be received.
if (srl_rx_lenght_param_addres == srl_rx_bytes_counter) {
len_temp = srl_rx_buf_pointer[srl_rx_bytes_counter];
// adding (or substracting) a length modifier
len_temp += srl_rx_lenght_param_modifier;
// if the target length is bigger than buffer size switch to error state
if (len_temp >= srl_rx_buf_ln) {
srl_rx_state = SRL_RX_ERROR;
stop_rxing = 1;
}
else {
srl_rx_bytes_req = len_temp;
}
}
// moving buffer pointer forward
srl_rx_bytes_counter++;
}
// if the user want the driver to stop receiving after certain is received
if (srl_triggered_stop == 1) {
if (srl_rx_buf_pointer[srl_rx_bytes_counter - 1] == srl_stop_trigger) {
srl_rx_state = SRL_RX_DONE;
stop_rxing = 1;
}
}
// if after incrementing a pointer we reached the end of the buffer
if (srl_rx_bytes_counter >= srl_rx_bytes_req) {
// enabling a flag to disble receiver
stop_rxing = 1;
// setting a state to receive done
srl_rx_state = SRL_RX_DONE;
}
if (stop_rxing == 1) {
srl_rx_timeout_calc_started = 0;
// disabling UART receiver and its interrupt
PORT->CR1 &= (0xFFFFFFFF ^ USART_CR1_RE);
PORT->CR1 &= (0xFFFFFFFF ^ USART_CR1_RXNEIE);
}
break;
}
// the state when a driver is waiting for start character to appear on serial link
case SRL_WAITING_TO_RX: {
// storing the value of DR register into local variable to protect against data races
// which may happened if this IT routine will be preempted by another (long) one
value = (uint8_t)PORT->DR;
// checking if start character was received
if (value == srl_start_trigger) {
// storing received byte in buffer as firts one
srl_rx_buf_pointer[srl_rx_bytes_counter] = value;
// increasing the counter value
srl_rx_bytes_counter++;
// change state to receiving
srl_rx_state = SRL_RXING;
}
else {
// if this is not start byte just store it in garbage buffer to clear interrupt condition
srl_garbage_storage = value;
}
break;
}
default: break;
}
}
// if one byte was successfully transferred from DR to shift register for transmission over USART
if ((PORT->SR & USART_SR_TXE) == USART_SR_TXE) {
switch (srl_tx_state) {
case SRL_TXING:
if (srl_tx_bytes_counter < srl_tx_bytes_req) {
PORT->DR = srl_tx_buf_pointer[srl_tx_bytes_counter++];
}
else {
while((PORT->SR & USART_SR_TC) != USART_SR_TC);
PORT->CR1 &= (0xFFFFFFFF ^ USART_CR1_TE); //wyġṗczanie nadajnika portu szeregowego
PORT->CR1 &= (0xFFFFFFFF ^ USART_CR1_TXEIE);
PORT->CR1 &= (0xFFFFFFFF ^ USART_CR1_TCIE); // wyġṗczanie przerwañ od portu szeregowego
PORT->SR &= (0xFFFFFFFF ^ USART_SR_TC);
srl_tx_state = SRL_TX_IDLE;
}
break;
default: break;
}
}
}
uint8_t* srl_get_rx_buffer() {
return srl_rx_buf_pointer;
}
void srl_switch_timeout(uint8_t disable_enable) {
if (disable_enable == 1)
srl_rx_timeout_enable = 1;
else if (disable_enable == 0)
srl_rx_timeout_enable = 0;
else;
}