forked from timypik/STM8S-Library
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrs485.c
359 lines (306 loc) · 10.8 KB
/
rs485.c
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
/**
******************************************************************************
* @file rs485.c
* @author Khusainov Timur
* @version 0.0.0.1
* @date 18.01.2012
* @brief
******************************************************************************
* @attention
* <h2><center>© COPYRIGHT [email protected] </center></h2>
******************************************************************************
*/
#include <stdint.h>
#include <stddef.h>
#include "board.h"
#include "irq/interrupt.h"
#include "rs485.h"
//------------------------------------------------------------------------------
#define _UART_EnableTx() do { UART3->SR = 0; UART3->CR2 |= UART3_CR2_TEN; UART3->CR2 |= UART3_CR2_TCIEN; } while(0)
#define _UART_EnableRx() do { UART3->SR = 0; UART3->CR2 |= UART3_CR2_REN; UART3->CR2 |= UART3_CR2_RIEN; } while(0)
#define _UART_DisableTx() do { UART3->CR2 &= ~UART3_CR2_TCIEN; UART3->CR2 &= ~UART3_CR2_TEN; } while(0)
#define _UART_DisableRx() do { UART3->CR2 &= ~UART3_CR2_RIEN; UART3->CR2 &= ~UART3_CR2_REN; } while(0)
//------------------------------------------------------------------------------
#define _UART_GetData() (UART3->DR)
#define _UART_GetError() (UART3->SR & (UART3_SR_PE|UART3_SR_FE|UART3_SR_NF|UART3_SR_OR))
#define _UART_SetData(_data) do { UART3->DR = (uint8_t)(_data); } while(0)
//------------------------------------------------------------------------------
#define _UART_CheckEventRx() (UART3->SR & (UART3_SR_RXNE|UART3_SR_PE|UART3_SR_FE|UART3_SR_NF|UART3_SR_OR))
#define _UART_CheckEventTx() (UART3->SR & UART3_SR_TC)
#define _UART_ResetEventRx() do { UART3->SR &= ~(UART3_SR_RXNE|UART3_SR_PE|UART3_SR_FE|UART3_SR_NF|UART3_SR_OR); } while (0)
#define _UART_ResetEventTx() do { UART3->SR &= ~(UART3_SR_TC|UART3_SR_TXE); } while (0)
//------------------------------------------------------------------------------
#define _IST_Enable() do { TIM3->CR1 |= TIM3_CR1_CEN; } while (0)
#define _IST_Disable() do { TIM3->CR1 &=~TIM3_CR1_CEN; } while (0)
#define _IST_ResetCounter() do { TIM3->CNTRH = 0; TIM3->CNTRL = 0; } while (0)
#define _IST_ResetStatus() do { TIM3->SR1 = 0; TIM3->SR2 = 0; } while (0)
#define _IST_CheckEvent() (TIM3->SR1 & TIM3_SR1_UIF)
//------------------------------------------------------------------------------
tRS485_OnAction RS485_OnAction = NULL;
//------------------------------------------------------------------------------
static size_t RS485_CountTx = 0;
static size_t RS485_CountRx = 0;
static uint8_t * RS485_DataTx = NULL;
static uint8_t * RS485_DataRx = NULL;
//------------------------------------------------------------------------------
volatile static enum
{
RS485_STATE_NOINIT = 0,
RS485_STATE_IDLE,
RS485_STATE_WAIT_TX,
RS485_STATE_WAIT_RX,
RS485_STATE_COMPLITE_TX,
RS485_STATE_COMPLITE_RX,
RS485_STATE_ERROR_TX,
RS485_STATE_ERROR_RX,
} RS485_State = RS485_STATE_NOINIT;
//------------------------------------------------------------------------------
typedef union
{
uint16_t Result;
struct
{
uint16_t BRR2_03 :4;
uint16_t BRR1 :8;
uint16_t BRR2_47 :4;
};
} tRS485_BaudCalc;
static const struct
{
uint32_t Speed;
tRS485_BaudCalc Result;
} RS485_BAUDRATE[RS485_SPEED_BAUDRATE_COUNT] =
{
/* recommended calculation at Reference Manual for STM8S */
{ 1200, (uint16_t)(((float)F_CPU/1200.0) + 0.5) },
{ 4800, (uint16_t)(((float)F_CPU/4800.0) + 0.5) },
{ 9600, (uint16_t)(((float)F_CPU/9600.0) + 0.5) },
{ 19200, (uint16_t)(((float)F_CPU/19200.0) + 0.5) },
{ 38400, (uint16_t)(((float)F_CPU/38400.0) + 0.5) },
{ 57600, (uint16_t)(((float)F_CPU/57600.0) + 0.5) },
{ 115200, (uint16_t)(((float)F_CPU/115200.0) + 0.5) },
{ 230400, (uint16_t)(((float)F_CPU/230400.0) + 0.5) },
};
//------------------------------------------------------------------------------
void _InitIntervalSymbolTimer(tRS485_Speed speed, tRS485_Parity parity, tRS485_Stopbit stopbits)
{
// Calc prescaler and autoreloader value for timer
/* 3,5 * ((Data(8) + Start-Stop(2) + Parity(1) + StopBit(1))/Badrate) */
float m_time_3_5bit = 3.5 * ((8 + 2 + (parity?1:0) + (stopbits?1:0))/((float)RS485_BAUDRATE[speed].Speed));
/* CPU clocks per 3,5 symbol */
uint32_t m_clk_3_5bit = (uint32_t)(F_CPU * m_time_3_5bit);
/* Timer value of prescaler: is power of 2 */
uint16_t m_pre = 0;
/* if num of clock > timer max value => up the prescaler */
while (m_clk_3_5bit/65536)
{
m_clk_3_5bit /= 2;
m_pre++;
}
/* Timer value of autoreloader */
uint16_t m_arl = (uint16_t)(m_clk_3_5bit);
// Configure timer
/* Disable all and enable one pulse mode */
TIM3->CR1 = TIM3_CR1_OPM;
/* Set prescaler */
TIM3->PSCR = m_pre;
/* Set autoreloader */
TIM3->ARRH = (uint8_t)(m_arl >> 8);
TIM3->ARRL = (uint8_t)(m_arl & 0xFF);
/* Generate an update event to reload the Prescaler value immediatly */
TIM3->EGR |= TIM3_EGR_UG;
/* Clear all flags */
_IST_ResetCounter();
_IST_ResetStatus();
/* Enable update interrupt */
TIM3->IER |= TIM3_IER_UIE;
/* Clear all flags */
_IST_ResetCounter();
_IST_ResetStatus();
}
//------------------------------------------------------------------------------
void RS485_IRQHandlerUART3_Rx()
{
static size_t m_data_count = 0;
if (_IST_CheckEvent())
{
_IST_ResetStatus();
RS485_CountRx = m_data_count;
m_data_count = 0;
_UART_DisableRx();
RS485_State = RS485_STATE_COMPLITE_RX;
}
else
{
_IST_Disable();
const uint8_t m_error = _UART_GetError();
const uint8_t m_data = _UART_GetData();
if (!m_error)
{
RS485_DataRx[m_data_count] = m_data;
m_data_count++;
if (m_data_count != RS485_CountRx)
{
_IST_ResetCounter();
_IST_ResetStatus();
_IST_Enable();
RS485_State = RS485_STATE_WAIT_RX;
}
else
{
RS485_CountRx = m_data_count;
m_data_count = 0;
_UART_DisableRx();
RS485_State = RS485_STATE_COMPLITE_RX;
}
}
else
{
RS485_CountRx = m_data_count;
m_data_count = 0;
_UART_DisableRx();
RS485_State = RS485_STATE_ERROR_RX;
}
_UART_ResetEventRx();
}
}
//------------------------------------------------------------------------------
void RS485_IRQHandlerUART3_Tx()
{
static size_t m_data_count = 0;
m_data_count++;
if (m_data_count != RS485_CountTx)
{
_UART_SetData(RS485_DataTx[m_data_count]);
RS485_State = RS485_STATE_WAIT_TX;
}
else
{
m_data_count = 0;
BSP_DIR485_Rx();
_UART_DisableTx();
RS485_State = RS485_STATE_COMPLITE_TX;
}
_UART_ResetEventTx();
}
//------------------------------------------------------------------------------
void BSP_InitRS485(tRS485_Speed speed, tRS485_Parity parity, tRS485_Stopbit stopbits, tRS485_OnAction pOnAction)
{
/* init direction control system */
BSP_InitD485();
BSP_DIR485_Rx();
//----------------------------------------------------------------------------
BSP_PIN_INPP(GPIOD, (1 << 5)); /* TX pin as input and pull-up */
BSP_PIN_INPP(GPIOD, (1 << 6)); /* RX pin as input and pull-up */
//----------------------------------------------------------------------------
UART3->CR1 = UART3_CR1_UARTD; /* disable UART: 8-bit, no parity */
UART3->CR2 = 0; /* reset register: disable transceiver and interrupts */
UART3->CR3 = 0; /* reset register: 1-stopbit */
UART3->CR4 = 0; /* reset register */
UART3->CR6 = 0; /* reset register */
tRS485_BaudCalc m_result = RS485_BAUDRATE[speed].Result;
/* set baudrate */
UART3->BRR2 = (uint8_t)(m_result.BRR2_47 << 4)|(m_result.BRR2_03 & 0x0f);
UART3->BRR1 = (uint8_t) m_result.BRR1;
switch (parity)
{
case RS485_PARITY_ODD:
UART3->CR1 |= UART3_CR1_PS;
case RS485_PARITY_EVEN:
UART3->CR1 |= (UART3_CR1_M|UART3_CR1_PCEN|UART3_CR1_PIEN);
break;
}
if (stopbits == RS485_STOPBIT_2)
UART3->CR3 |= (1 << 5);
/* set interrupt vector handlers */
BSP_InterruptSet(RS485_IRQHandlerUART3_Tx, BSP_IRQ_VECTOR_UART3_TX, bspitStd);
BSP_InterruptSet(RS485_IRQHandlerUART3_Rx, BSP_IRQ_VECTOR_UART3_RX, bspitStd);
//----------------------------------------------------------------------------
/* setup 3,5 interval timer */
_InitIntervalSymbolTimer(speed, parity, stopbits);
/* set 3,5 interval timer vector handler */
BSP_InterruptSet(RS485_IRQHandlerUART3_Rx, BSP_IRQ_VECTOR_TIM3_OVR_UIF, bspitStd);
//----------------------------------------------------------------------------
/* set action handlers */
RS485_OnAction = pOnAction;
RS485_State = RS485_STATE_IDLE;
}
//------------------------------------------------------------------------------
void BSP_SetPriority485(tBSP_Priority priority)
{
BSP_SetPriority(BSP_IRQ_VECTOR_UART3_TX, priority);
BSP_SetPriority(BSP_IRQ_VECTOR_UART3_RX, priority);
BSP_SetPriority(BSP_IRQ_VECTOR_TIM3_OVR_UIF, priority);
}
//------------------------------------------------------------------------------
void BSP_EnableRS485()
{
UART3->CR1 &=~ UART3_CR1_UARTD; /* disable UART */
RS485_State = RS485_STATE_IDLE;
}
//------------------------------------------------------------------------------
void BSP_DisableRS485()
{
UART3->CR1 |= UART3_CR1_UARTD; /* enable UART */
RS485_State = RS485_STATE_IDLE;
}
//------------------------------------------------------------------------------
void RS485_TransmitData(const uint8_t * data, size_t length)
{
RS485_DataTx = (uint8_t *)data;
RS485_CountTx = length;
RS485_State = RS485_STATE_WAIT_TX;
_UART_EnableTx();
BSP_DIR485_Tx();
_UART_SetData(data[0]);
}
//------------------------------------------------------------------------------
void RS485_ReceiveData(uint8_t * const data, size_t length)
{
RS485_DataRx = (uint8_t *)data;
RS485_CountRx = length;
RS485_State = RS485_STATE_WAIT_RX;
_UART_EnableRx();
BSP_DIR485_Rx();
}
//------------------------------------------------------------------------------
void RS485_Process()
{
switch (RS485_State)
{
case RS485_STATE_NOINIT:
case RS485_STATE_IDLE:
case RS485_STATE_WAIT_RX:
case RS485_STATE_WAIT_TX:
break;
case RS485_STATE_COMPLITE_RX:
{
RS485_State = RS485_STATE_IDLE;
if (RS485_OnAction)
RS485_OnAction(RS485_ACTION_DATA_RX, RS485_DataRx, RS485_CountRx);
}
break;
case RS485_STATE_COMPLITE_TX:
{
RS485_State = RS485_STATE_IDLE;
if (RS485_OnAction)
RS485_OnAction(RS485_ACTION_DATA_TX, RS485_DataTx, RS485_CountTx);
}
break;
case RS485_STATE_ERROR_RX:
{
RS485_State = RS485_STATE_IDLE;
if (RS485_OnAction)
RS485_OnAction(RS485_ACTION_ERROR_RX, NULL, RS485_CountRx);
}
break;
case RS485_STATE_ERROR_TX:
{
RS485_State = RS485_STATE_IDLE;
if (RS485_OnAction)
RS485_OnAction(RS485_ACTION_ERROR_TX, NULL, RS485_CountTx);
}
break;
}
}
//------------------------------------------------------------------------------