1 /*
2 * Copyright (c) 2023 HPMicro
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #include "hpm_uart_lin.h"
9
10 #ifndef HPM_UART_LIN_RETRY_COUNT
11 #define HPM_UART_LIN_RETRY_COUNT (50000U)
12 #endif
13
14 #ifndef HPM_UART_LIN_BREAK_LENGTH
15 #define HPM_UART_LIN_BREAK_LENGTH (13U) /* bits */
16 #endif
17
18 #ifndef HPM_UART_LIN_WAKEUP_LENGTH
19 #define HPM_UART_LIN_WAKEUP_LENGTH (400U) /* us */
20 #endif
21
22
hpm_uart_lin_calculate_protected_id(uint8_t id)23 uint8_t hpm_uart_lin_calculate_protected_id(uint8_t id)
24 {
25 uint8_t id0, id1, id2, id3, id4, id5, p0, p1, pid;
26
27 /* P0 = ID0 @ ID1 @ ID2 @ ID3 @ ID4 */
28 /* P1 = !(ID1 @ ID2 @ ID3 @ ID4 @ ID5) */
29 id0 = (id >> 0U) & 0x1U;
30 id1 = (id >> 1U) & 0x1U;
31 id2 = (id >> 2U) & 0x1U;
32 id3 = (id >> 3U) & 0x1U;
33 id4 = (id >> 4U) & 0x1U;
34 id5 = (id >> 5U) & 0x1U;
35
36 p0 = id0 ^ id1 ^ id2 ^ id4;
37 p1 = !(id1 ^ id3 ^ id4 ^ id5);
38 pid = (p1 << 7) | (p0 << 6) | id;
39 return pid;
40 }
41
hpm_uart_lin_calculate_checksum(uint8_t id,uint8_t * data,uint8_t length,bool enhanced_checksum)42 static uint8_t hpm_uart_lin_calculate_checksum(uint8_t id, uint8_t *data, uint8_t length, bool enhanced_checksum)
43 {
44 assert(length <= 8U);
45 uint8_t checksum = 0;
46 uint16_t temp;
47 for (uint8_t i = 0; i < length; i++) {
48 temp = checksum + data[i];
49 checksum += data[i] + (temp >> 8U);
50 }
51
52 if (enhanced_checksum) {
53 temp = checksum + id;
54 checksum += id + (temp >> 8U);
55 }
56
57 checksum = ~checksum;
58 return checksum;
59 }
60
hpm_uart_lin_check_checksum(uint8_t id,uint8_t * data,uint8_t length,bool enhanced_checksum,uint8_t checksum)61 static bool hpm_uart_lin_check_checksum(uint8_t id, uint8_t *data, uint8_t length, bool enhanced_checksum, uint8_t checksum)
62 {
63 uint8_t cal_checksum;
64 cal_checksum = hpm_uart_lin_calculate_checksum(id, data, length, enhanced_checksum);
65
66 if (cal_checksum != checksum) {
67 return false;
68 }
69 return true;
70 }
71
72
hpm_uart_lin_send_break(UART_Type * ptr,uart_lin_master_pin_ctrl_t * pin_ctrl)73 static void hpm_uart_lin_send_break(UART_Type *ptr, uart_lin_master_pin_ctrl_t *pin_ctrl)
74 {
75 assert(pin_ctrl->baudrate <= 20000);
76
77 uint32_t bit_period_us = 1000000 / pin_ctrl->baudrate;
78 uint32_t break_period_us = bit_period_us * HPM_UART_LIN_BREAK_LENGTH;
79 pin_ctrl->config_uart_pin_as_gpio(ptr);
80 gpio_set_pin_output(pin_ctrl->ptr, pin_ctrl->tx_port, pin_ctrl->tx_pin);
81 gpio_write_pin(pin_ctrl->ptr, pin_ctrl->tx_port, pin_ctrl->tx_pin, 0);
82 pin_ctrl->delay_us(break_period_us);
83 gpio_write_pin(pin_ctrl->ptr, pin_ctrl->tx_port, pin_ctrl->tx_pin, 1);
84 pin_ctrl->delay_us(bit_period_us);
85 pin_ctrl->config_uart_pin(ptr);
86 }
87
hpm_uart_lin_send_sync(UART_Type * ptr)88 static void hpm_uart_lin_send_sync(UART_Type *ptr)
89 {
90 uart_write_byte(ptr, 0x55); /* sync phase */
91 }
92
hpm_uart_lin_send_wakeup(UART_Type * ptr,uart_lin_master_pin_ctrl_t * pin_ctrl)93 void hpm_uart_lin_send_wakeup(UART_Type *ptr, uart_lin_master_pin_ctrl_t *pin_ctrl)
94 {
95 pin_ctrl->config_uart_pin_as_gpio(ptr);
96 gpio_set_pin_output(pin_ctrl->ptr, pin_ctrl->tx_port, pin_ctrl->tx_pin);
97 gpio_write_pin(pin_ctrl->ptr, pin_ctrl->tx_port, pin_ctrl->tx_pin, 0);
98 pin_ctrl->delay_us(HPM_UART_LIN_WAKEUP_LENGTH);
99 gpio_write_pin(pin_ctrl->ptr, pin_ctrl->tx_port, pin_ctrl->tx_pin, 1);
100 pin_ctrl->config_uart_pin(ptr);
101 }
102
hpm_uart_lin_master_send_frame(uart_lin_master_config_t * config)103 uart_lin_stat_t hpm_uart_lin_master_send_frame(uart_lin_master_config_t *config)
104 {
105 uint32_t retry;
106 UART_Type *ptr = config->ptr;
107 uart_lin_data_t data = config->data;
108 uint8_t pid = hpm_uart_lin_calculate_protected_id(config->id);
109 uint8_t checksum = hpm_uart_lin_calculate_checksum(pid, data.buff, data.length, data.enhance_checksum);
110 uint8_t send_data[11] = {0}; /* max 8 data bytes + 1 byte 0x55 + 1 byte pid + 1byte checksum */
111 uint8_t length = data.length + 3;
112
113 assert(data.length > 0);
114
115 /* 0x55 - pid - data - checksum */
116 send_data[0] = 0x55;
117 send_data[1] = pid;
118 memcpy(&send_data[2], data.buff, data.length);
119 send_data[data.length + 2] = checksum;
120
121 hpm_uart_lin_send_break(ptr, &(config->pin_ctrl));
122
123 for (uint8_t i = 0; i < length; i++) {
124 retry = 0;
125 while (!uart_check_status(ptr, uart_stat_tx_slot_avail)) {
126 if (retry > HPM_UART_LIN_RETRY_COUNT) {
127 break;
128 }
129 retry++;
130 }
131
132 if (retry > HPM_UART_LIN_RETRY_COUNT) {
133 return uart_lin_timeout;
134 }
135
136 uart_write_byte(ptr, send_data[i]);
137 }
138
139 return uart_lin_success;
140 }
141
142
hpm_uart_lin_master_receive_frame(uart_lin_master_config_t * config)143 uart_lin_stat_t hpm_uart_lin_master_receive_frame(uart_lin_master_config_t *config)
144 {
145 uint32_t retry = 0;
146 UART_Type *ptr = config->ptr;
147 uart_lin_data_t data = config->data;
148 uint8_t pid = hpm_uart_lin_calculate_protected_id(config->id);
149 uint8_t checksum;
150 uint8_t *buff = data.buff;
151
152 assert(data.length > 0);
153
154 /* clear data in rx fifo */
155 uart_clear_rx_fifo(ptr);
156
157 hpm_uart_lin_send_break(ptr, &(config->pin_ctrl));
158
159 hpm_uart_lin_send_sync(ptr);
160
161 uart_write_byte(ptr, pid);
162
163 /* wait for send 0x55 and pid */
164 while (!uart_check_status(ptr, uart_stat_tx_slot_avail)) {
165 if (retry > HPM_UART_LIN_RETRY_COUNT * 2) {
166 break;
167 }
168 retry++;
169 }
170
171 if (retry > HPM_UART_LIN_RETRY_COUNT * 2) {
172 return uart_lin_timeout;
173 }
174
175 /* wait for receive complete */
176 for (uint8_t i = 0; i < data.length + 3; i++) {
177 retry = 0;
178 while (!uart_check_status(ptr, uart_stat_data_ready)) {
179 if (retry > HPM_UART_LIN_RETRY_COUNT) {
180 break;
181 }
182 retry++;
183 }
184
185 if (retry > HPM_UART_LIN_RETRY_COUNT) {
186 return uart_lin_timeout;
187 }
188
189 if (i < 2) {
190 uart_read_byte(ptr);
191 } else if (i < data.length + 2) {
192 *(buff++) = uart_read_byte(ptr);
193 } else {
194 checksum = uart_read_byte(ptr);
195 }
196 }
197
198 if (!hpm_uart_lin_check_checksum(pid, data.buff, data.length, data.enhance_checksum, checksum)) {
199 return uart_lin_checksum_error;
200 }
201
202 return uart_lin_success;
203 }
204
205 /* generate break with gpio then write 0x55 and pid into uart tx fifo */
hpm_uart_lin_master_send_head(uart_lin_master_config_t * config)206 void hpm_uart_lin_master_send_head(uart_lin_master_config_t *config)
207 {
208 UART_Type *ptr = config->ptr;
209 uint8_t pid = hpm_uart_lin_calculate_protected_id(config->id);
210
211 /* clear data in rx fifo */
212 uart_clear_rx_fifo(ptr);
213
214 hpm_uart_lin_send_break(ptr, &(config->pin_ctrl));
215
216 hpm_uart_lin_send_sync(ptr);
217
218 uart_write_byte(ptr, pid);
219 }
220
221 /* write data into uart tx fifo including data and checksum */
hpm_uart_lin_master_send_data(uart_lin_master_config_t * config)222 void hpm_uart_lin_master_send_data(uart_lin_master_config_t *config)
223 {
224 UART_Type *ptr = config->ptr;
225 uart_lin_data_t data = config->data;
226
227 assert(data.length > 0);
228
229 uint8_t pid = hpm_uart_lin_calculate_protected_id(config->id);
230 uint8_t checksum = hpm_uart_lin_calculate_checksum(pid, data.buff, data.length, data.enhance_checksum);
231
232 for (uint8_t i = 0; i < data.length; i++) {
233 uart_write_byte(ptr, *(data.buff++));
234 }
235
236 uart_write_byte(ptr, checksum);
237 }
238
239 /* call this function in rx timeout isr */
240 /* read data from uart rx fifo */
hpm_uart_lin_master_receive_data(uart_lin_master_config_t * config)241 uart_lin_stat_t hpm_uart_lin_master_receive_data(uart_lin_master_config_t *config)
242 {
243 UART_Type *ptr = config->ptr;
244 uart_lin_data_t data = config->data;
245 uint8_t pid = hpm_uart_lin_calculate_protected_id(config->id);
246 uint8_t checksum = 0;
247 uint8_t index = 0;
248 uint8_t *buff = data.buff;
249
250 assert(data.length > 0);
251
252 while (uart_check_status(ptr, uart_stat_data_ready)) {
253 if (index >= data.length + 3) {
254 break;
255 }
256 if (index < 2) {
257 uart_read_byte(ptr); /* read 0x55 and pid */
258 } else if (index < data.length + 2) {
259 *(buff++) = uart_read_byte(ptr);
260 } else {
261 checksum = uart_read_byte(ptr);
262 }
263 index++;
264 }
265
266 if (index != data.length + 3) {
267 return uart_lin_frame_error;
268 }
269
270 if (!hpm_uart_lin_check_checksum(pid, data.buff, data.length, data.enhance_checksum, checksum)) {
271 return uart_lin_checksum_error;
272 }
273
274 return uart_lin_success;
275 }
276
277 /* write data into uart tx fifo including data and checksum */
hpm_uart_lin_slave_send_data(uart_lin_slave_config_t * config)278 void hpm_uart_lin_slave_send_data(uart_lin_slave_config_t *config)
279 {
280 UART_Type *ptr = config->ptr;
281 uart_lin_data_t data = config->data;
282
283 assert(data.length > 0);
284
285 uint8_t checksum = hpm_uart_lin_calculate_checksum(config->pid, data.buff, data.length, data.enhance_checksum);
286
287 for (uint8_t i = 0; i < data.length; i++) {
288 uart_write_byte(ptr, *(data.buff++));
289 }
290
291 uart_write_byte(ptr, checksum);
292 }
293
294 /* read data and checksum */
hpm_uart_lin_slave_receive_data(uart_lin_slave_config_t * config)295 uart_lin_stat_t hpm_uart_lin_slave_receive_data(uart_lin_slave_config_t *config)
296 {
297 UART_Type *ptr = config->ptr;
298 uart_lin_data_t data = config->data;
299
300 assert(data.length > 0);
301
302 uint8_t index = 0;
303 uint8_t checksum = 0;
304
305 /* receive data and checksum */
306 while (uart_check_status(ptr, uart_stat_data_ready)) {
307 if (index == data.length) {
308 checksum = uart_read_byte(ptr);
309 break;
310 }
311 *(data.buff + index++) = uart_read_byte(ptr);
312 }
313
314 if (index != data.length) {
315 return uart_lin_frame_error;
316 }
317
318 if (!hpm_uart_lin_check_checksum(config->pid, data.buff, data.length, data.enhance_checksum, checksum)) {
319 return uart_lin_checksum_error;
320 }
321
322 return uart_lin_success;
323 }