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