2 * PCAN-LIN, RS-232 to CAN/LIN converter control application
4 * This program is free software; you can distribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; version 2 of
9 * Copyright: (c) 2012 Czech Technical University in Prague
10 * Authors: Rostislav Lisovy <lisovy@gmail.cz>
24 #define PCL_PKT_MAX_SIZE 16
25 #define PCL_HEADERS_SIZE 2 /* There are 2 bytes of headers */
26 #define PCL_CHECKSUM_SIZE 1
27 #define PCL_STX_SIZE 1
28 #define PCL_PACKET_OVERHEAD (PCL_HEADERS_SIZE + PCL_CHECKSUM_SIZE)
30 /* Logical representation of a packet sent to PCAN-LIN converter via RS232 */
32 uint8_t stx; /* Start transmission; Always set to 0x2 */
33 uint8_t seq_no; /* Sequence number */
34 uint8_t seq_frlen; /* Frame length */
35 uint8_t ctrl_tiface;/* Target interface */
36 uint8_t ctrl_comc; /* Command code */
37 uint8_t parms[8]; /* Parameters; Number of parameters depends
38 on the frame length */
39 uint8_t chks; /* Checksum; Bitwise XOR of all bytes except STX */
44 #define PCL_SEQ_NO_ofs 4
45 #define PCL_SEQ_FRLEN_ofs 0
46 #define PCL_CTRL_TIFACE_ofs 6
47 #define PCL_CTRL_COMC_ofs 0
49 #define PCL_SEQ_FRLEN_msk 0xF
51 #define PCL_PACKET_LIN_IFACE 0x2
52 #define PCL_PACKET_MODULE_IFACE 0x3
54 struct termios term_attr;
56 struct pcl_scheduler_entry_t {
59 } pcl_scheduler_entry[] = {
70 #define PCL_UNACTIVE 0
71 /* Excerpt from the PCAN-LIN documentation:
73 The length of the data depends on the slave ID. If the used frame
74 length is the default, the length to be used can be found using
76 ID Range | Data Length
77 ----------------+---------------
78 0x00 to 0x1F | 2 Bytes
79 0x20 to 0x2F | 4 Bytes
80 0x30 to 0x3F | 8 Bytes
82 If the frame length of the used LIN ID was configured with another
83 length, than that length must be used;
86 /* Index in this array = LIN ID */
87 struct pcl_publish_entry_t {
88 int status; /* 1 = active; 0 = unactive */
90 unsigned char data[8];
91 } pcl_publish_entry[] = {
92 [0x0] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
93 [0x1] = { PCL_ACTIVE, 2, {0xff, 0xff} },
94 [0x2] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
95 [0x3] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
96 [0x4] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
97 [0x5] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
98 [0x6] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
99 [0x7] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
100 [0x8] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
101 [0x9] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
102 [0xa] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
103 [0xb] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
104 [0xc] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
105 [0xd] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
106 [0xe] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
107 [0xf] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
108 [0x10] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
109 [0x11] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
110 [0x12] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
111 [0x13] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
112 [0x14] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
113 [0x15] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
114 [0x16] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
115 [0x17] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
116 [0x18] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
117 [0x19] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
118 [0x1a] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
119 [0x1b] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
120 [0x1c] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
121 [0x1d] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
122 [0x1e] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
123 [0x1f] = { PCL_UNACTIVE, 2, {0xff, 0xff} },
125 [0x20] = { PCL_UNACTIVE, 4, {0xff, 0xff, 0xff, 0xff} },
126 [0x21] = { PCL_UNACTIVE, 4, {0xff, 0xff, 0xff, 0xff} },
127 [0x22] = { PCL_UNACTIVE, 4, {0xff, 0xff, 0xff, 0xff} },
128 [0x23] = { PCL_UNACTIVE, 4, {0xff, 0xff, 0xff, 0xff} },
129 [0x24] = { PCL_UNACTIVE, 4, {0xff, 0xff, 0xff, 0xff} },
130 [0x25] = { PCL_UNACTIVE, 4, {0xff, 0xff, 0xff, 0xff} },
131 [0x26] = { PCL_UNACTIVE, 4, {0xff, 0xff, 0xff, 0xff} },
132 [0x27] = { PCL_UNACTIVE, 4, {0xff, 0xff, 0xff, 0xff} },
133 [0x28] = { PCL_UNACTIVE, 4, {0xff, 0xff, 0xff, 0xff} },
134 [0x29] = { PCL_UNACTIVE, 4, {0xff, 0xff, 0xff, 0xff} },
135 [0x2a] = { PCL_UNACTIVE, 4, {0xff, 0xff, 0xff, 0xff} },
136 [0x2b] = { PCL_UNACTIVE, 4, {0xff, 0xff, 0xff, 0xff} },
137 [0x2c] = { PCL_UNACTIVE, 4, {0xff, 0xff, 0xff, 0xff} },
138 [0x2d] = { PCL_UNACTIVE, 4, {0xff, 0xff, 0xff, 0xff} },
139 [0x2e] = { PCL_UNACTIVE, 4, {0xff, 0xff, 0xff, 0xff} },
140 [0x2f] = { PCL_UNACTIVE, 4, {0xff, 0xff, 0xff, 0xff} },
142 [0x30] = { PCL_UNACTIVE, 6, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} },
143 [0x31] = { PCL_UNACTIVE, 6, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} },
144 [0x32] = { PCL_UNACTIVE, 6, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} },
145 [0x33] = { PCL_UNACTIVE, 6, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} },
146 [0x34] = { PCL_UNACTIVE, 6, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} },
147 [0x35] = { PCL_UNACTIVE, 6, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} },
148 [0x36] = { PCL_UNACTIVE, 6, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} },
149 [0x37] = { PCL_UNACTIVE, 6, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} },
150 [0x38] = { PCL_UNACTIVE, 6, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} },
151 [0x39] = { PCL_UNACTIVE, 6, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} },
152 [0x3a] = { PCL_UNACTIVE, 6, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} },
153 [0x3b] = { PCL_UNACTIVE, 6, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} },
154 [0x3c] = { PCL_UNACTIVE, 6, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} },
155 [0x3d] = { PCL_UNACTIVE, 6, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} },
156 [0x3e] = { PCL_UNACTIVE, 6, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} },
157 [0x3f] = { PCL_UNACTIVE, 6, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} },
160 int pcl_slave_only_fl = false;
161 /* ------------------------------------------------------------------------ */
163 /* Transform 'logic' representation of a frame to exact byte sequence */
164 int pcl_serialize(pcl_packet_t *pkt, uint8_t *pkt_raw)
167 uint8_t checksum = 0;
169 pkt_raw[0] = pkt->stx;
170 pkt_raw[1] = (pkt->seq_no << PCL_SEQ_NO_ofs) |
171 (pkt->seq_frlen << PCL_SEQ_FRLEN_ofs);
172 pkt_raw[2] = (pkt->ctrl_tiface << PCL_CTRL_TIFACE_ofs) |
173 (pkt->ctrl_comc << PCL_CTRL_COMC_ofs);
175 for (i = 0; i < pkt->seq_frlen; i++) {
176 pkt_raw[3+i] = pkt->parms[i];
179 /* Calculate XOR checksum; Skip STX */
180 for (i = 1; i <= pkt->seq_frlen + PCL_HEADERS_SIZE; i++) {
181 checksum ^= pkt_raw[i];
183 pkt_raw[i] = checksum;
185 printf("Snd: [STX] [SEQ] [PRM] [...] \n ");
186 for (i = 0; i < pkt->seq_frlen + PCL_PACKET_OVERHEAD + 1; i++)
187 printf("0x%02X ", pkt_raw[i]);
190 return pkt->seq_frlen + 4; /* real packet size */
193 int pcl_send_frame(int tty, pcl_packet_t *pkt)
198 uint8_t pkt_buff[PCL_PKT_MAX_SIZE];
200 pkt_size = pcl_serialize(pkt, (uint8_t *) &pkt_buff);
204 while (to_send > 0) {
205 sent = write(tty, pkt_buff+sent, to_send);
209 memset(pkt, '\0', sizeof(pcl_packet_t));
213 int pcl_read_reset_response(int tty)
218 char str_match[] = {'G', 'm', 'b', 'H'};
221 ret = read(tty, &c, 1);
227 if ((c == str_match[i]) && (i == 3))
228 i = -1; /* We are done -- Stop the loop*/
229 else if (c == str_match[i])
230 i++; /* Match found -- Go to the next letter */
232 i = 0; /* Start from beginning */
241 int pcl_read_response(int tty)
247 uint8_t buff[PCL_PKT_MAX_SIZE];
248 //memset(buff, '\0', sizeof(buff));
250 /* Read 2 bytes of header */
251 received = read(tty, &buff[0], 1); /* Read STX */
255 if (buff[0] == 0x2) { /* STX ok */
256 received = read(tty, &buff[1], 1); /* Read SEQ */
263 data_length = (buff[1] & PCL_SEQ_FRLEN_msk);
264 to_read = data_length + 1; /* +chksm */
265 while (to_read > 0) {
266 received = read(tty, &buff[2], to_read);
270 printf("Rcv: [STX] [SEQ] [PRM] [CHK]\n"); // FIXME add spaces before "CHKS" when
271 //more than 1 byte received in params
272 printf(" 0x%02X 0x%02X", buff[0], buff[1]);
273 for (i = 0; i < data_length; i++) {
274 printf(" 0x%02X", buff[i + 2]);
276 printf(" 0x%02X\n\n", buff[i]);
281 void pcl_insert_scheduler_entries(int tty)
286 /* Insert scheduler entry */
287 for (i = 0; i < (sizeof(pcl_scheduler_entry)/sizeof(struct pcl_scheduler_entry_t)); i++) {
291 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
292 pkt.ctrl_comc = 0x28;
293 pkt.parms[0] = (pcl_scheduler_entry[i].interval_ms) & 0xFF;
294 pkt.parms[1] = (pcl_scheduler_entry[i].interval_ms >> 8) & 0xFF;
295 pkt.parms[2] = pcl_scheduler_entry[i].lin_id; /* LIN ID */
297 pcl_send_frame(tty, &pkt);
298 pcl_read_response(tty);
303 void pcl_set_slave_id_and_data_configuration(int tty)
308 /* Set Slave ID + Data Configuration */
309 for (i = 0; i < 0x3F; i++) {
312 if (pcl_publish_entry[i].status == PCL_ACTIVE) {
315 pkt.seq_frlen = pcl_publish_entry[i].data_len + 2;
316 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
317 pkt.ctrl_comc = 0x29;
318 pkt.parms[0] = pcl_publish_entry[i].status; /* Field Status */
319 pkt.parms[1] = i; /* LIN ID */
320 pkt.parms[2] = pcl_publish_entry[i].data[0]; /* Data */
321 pkt.parms[3] = pcl_publish_entry[i].data[1]; /* Data */
322 pkt.parms[4] = pcl_publish_entry[i].data[2]; /* Data */
323 pkt.parms[5] = pcl_publish_entry[i].data[3]; /* Data */
324 pkt.parms[6] = pcl_publish_entry[i].data[4]; /* Data */
325 pkt.parms[7] = pcl_publish_entry[i].data[5]; /* Data */
326 pkt.parms[8] = pcl_publish_entry[i].data[6]; /* Data */
327 pkt.parms[9] = pcl_publish_entry[i].data[7]; /* Data */
340 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
341 pkt.ctrl_comc = 0x29;
342 pkt.parms[0] = 0x00; /* Field Status -- unactive */
343 pkt.parms[1] = i; /* LIN ID */
346 pcl_send_frame(tty, &pkt);
347 pcl_read_response(tty);
351 void pcl_flash_config(int tty)
355 /* Flash Current Configuration */
359 pkt.ctrl_tiface = PCL_PACKET_MODULE_IFACE;
362 pcl_send_frame(tty, &pkt);
363 pcl_read_response(tty);
366 void pcl_reset_device(int tty)
374 pkt.ctrl_tiface = PCL_PACKET_MODULE_IFACE;
377 pcl_send_frame(tty, &pkt);
378 pcl_read_reset_response(tty);
381 int pcl_lin_init(int tty)
385 /* Initialization according to current parameters */
389 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
392 pcl_send_frame(tty, &pkt);
393 pcl_read_response(tty);
397 pcl_reset_device(tty);
399 /* Erase Master Scheduler List */
403 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
404 pkt.ctrl_comc = 0x33;
406 pcl_send_frame(tty, &pkt);
407 pcl_read_response(tty);
409 /* Set Activation Status */
413 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
414 pkt.ctrl_comc = 0x1E;
417 pcl_send_frame(tty, &pkt);
418 pcl_read_response(tty);
424 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
425 pkt.ctrl_comc = 0x1F;
427 pkt.parms[1] = 0x4B; /* 19200 kBit/s */
429 pcl_send_frame(tty, &pkt);
430 pcl_read_response(tty);
432 /* Set Forward Mask */
436 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
437 pkt.ctrl_comc = 0x20;
440 pcl_send_frame(tty, &pkt);
441 pcl_read_response(tty);
443 /* Set Filter Mask */
447 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
448 pkt.ctrl_comc = 0x21;
451 pcl_send_frame(tty, &pkt);
452 pcl_read_response(tty);
454 /* Set Filter Code */
458 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
459 pkt.ctrl_comc = 0x21;
462 pcl_send_frame(tty, &pkt);
463 pcl_read_response(tty);
465 /* Set Message Transmission Timeouts */
469 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
470 pkt.ctrl_comc = 0x26;
480 pcl_send_frame(tty, &pkt);
481 pcl_read_response(tty);
483 /* Set Message Retries */
487 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
488 pkt.ctrl_comc = 0x27;
491 pcl_send_frame(tty, &pkt);
492 pcl_read_response(tty);
494 /* Set Slave ID + Data configuration */
495 pcl_set_slave_id_and_data_configuration(tty);
497 /* Insert scheduler entry */
498 pcl_insert_scheduler_entries(tty);
500 /* Set master status: Active */
504 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
505 pkt.ctrl_comc = 0x24;
506 pkt.parms[0] = (pcl_slave_only_fl) ? 0x0 : 0x1;
508 pcl_send_frame(tty, &pkt);
509 pcl_read_response(tty);
511 /* Set LIN bus termination */
515 pkt.ctrl_tiface = PCL_PACKET_LIN_IFACE;
516 pkt.ctrl_comc = 0x25;
517 pkt.parms[0] = (pcl_slave_only_fl) ? 0x0 : 0x1;
518 /* Should have the same value as "Set master status" */
520 pcl_send_frame(tty, &pkt);
521 pcl_read_response(tty);
526 static void pcl_reset_input_mode(int tty)
528 tcsetattr(tty, TCSANOW, &term_attr);
531 static void pcl_set_input_mode(int tty)
534 struct termios tattr;
536 /* Flush input and output queues. */
537 if (tcflush(tty, TCIOFLUSH) != 0) {
542 /* Fetch the current terminal parameters. */
544 fprintf(stderr, "Not a terminal.\n");
548 /* Save settings for later restoring */
549 tcgetattr(tty, &term_attr);
552 tcgetattr(tty, &tattr);
553 tattr.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
554 | INLCR | IGNCR | ICRNL | IXON);
555 tattr.c_oflag &= ~OPOST;
556 tattr.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
557 tattr.c_cflag &= ~(CSIZE | PARENB);
558 tattr.c_cflag |= CS8;
560 tattr.c_cc[VMIN] = 1;
561 tattr.c_cc[VTIME] = 0;
563 /* Set TX, RX speed */
564 cfsetispeed(&tattr, B38400);
565 cfsetospeed(&tattr, B38400);
567 status = tcsetattr(tty, TCSANOW, &tattr);
569 perror("tcsetattr()");
573 void pcl_explain(int argc, char *argv[])
575 fprintf(stderr, "Usage: %s [OPTIONS] <SERIAL_INTERFACE>\n", argv[0]);
576 fprintf(stderr, "\n");
577 fprintf(stderr, "'pcan_lin_config' Is used for configuring PEAK PCAN-LIN device.\n");
578 fprintf(stderr, " When invoked without any OPTIONS, it configures PCAN-LIN device\n");
579 fprintf(stderr, " with default configuration from the program.\n");
580 fprintf(stderr, " The PCAN-LIN module enables CAN, LIN and serial participants to communicate.\n");
581 fprintf(stderr, "\n");
582 fprintf(stderr, "Options:\n");
583 fprintf(stderr, " -r Execute only Reset of a device\n");
584 fprintf(stderr, " -f Flash the active configuration\n");
585 fprintf(stderr, " -s Activate only LIN Slave node in the device\n");
586 fprintf(stderr, "\n");
587 fprintf(stderr, "Examples:\n");
588 fprintf(stderr, " %s /dev/ttyS0 (Configure the device with the default configuration)\n", argv[0]);
589 fprintf(stderr, " %s -r /dev/ttyS0 (Reset the device)\n", argv[0]);
592 int main(int argc, char *argv[])
594 char dev[32]; // FIXME
597 int pcl_reset_device_fl = false;
598 int pcl_flash_config_fl = false;
600 while ((opt = getopt(argc, argv, "rfs")) != -1) {
603 pcl_reset_device_fl = true;
606 pcl_flash_config_fl = true;
609 pcl_slave_only_fl = true;
612 pcl_explain(argc, argv);
617 /* Expected argument after options */
618 if (optind >= argc) {
619 pcl_explain(argc, argv);
623 strncpy((char *) &dev, argv[optind], 32);
624 tty = open(dev, O_RDWR);
631 pcl_set_input_mode(tty);
633 if (pcl_reset_device_fl) {
634 pcl_reset_device(tty);
640 if (pcl_flash_config_fl) {
641 pcl_flash_config(tty);
642 pcl_reset_device(tty);
646 pcl_reset_input_mode(tty);