LIN bus 2.1, working example?

PaulS

Well-known member
Forum member @jsimonkeller and I are struggling to get a LIN bus 2.1 device to work, see this thread.
We both do not have experience with this library and also do not know whether LIN bus V2.1 frames are actually working.
So I hope someone with experience (@KurtE, @skpang ?) can chime in and give us some advice.

Thanks,
Paul
 
Sorry never used it... Have never sed LIN bus. I changed to allow a two step startup as per some other thread...
 
Thanks guys. I have made progress with Paul's help, but stuck again.

0
0
FC
17
0
20
8
0

Not sure which of these should change if getting feedback from the heater it is ON.

Also, I am still unsure if I need to send a command of 8 to turn it on and which line does this in the code?

I purchased the RGB board to test SK Pang's code when it arrives next week.
 
Ordered a LIN bus board and RGB board yesterday as well from SK Pang. Hope that they arrive end of this week. Then I will look into the LIN stuff again.

Paul
 
Last edited:
Thanks Paul. Hopefully, we can sort this out together. I really appreciate your help. I ordered and oscilloscope as well that should arrive this week.
 
UPDATE: New CAN FD LIN breakout board arrived today after I shorted out my teensy, BUT when I hooked up the new teensy and breakout board with the same RGB DEMO SKETCH and same wiring, after compiling the code to the new teensy, the RGB lin slave board is outputting Red Green and Blue!!!!!

I am not sure if that means the prior Teensy or breakout board had a defect, but I am encouraged that this means the heater code might actually work when I give that a try!!

https://photos.app.goo.gl/wWHi2c58oPyurFUU8

I will see what happens tomorrow. Maybe all of that code you were working on will finally bear some fruit Paul!
 
Good to hear that your boards are working. Looking forward to your actual heater results!

Paul
 
Hi Paul. So I know the new board is working and the following code produces a lin signal that does engage the Red Blue and Green lights on the slave board.

Code:
/*
 *
 * Teensy 4.0 CAN FD and LIN-bus demo.
 *
 * For use with this board:
 * https://www.skpang.co.uk/products/teensy-4-0-can-fd-and-lin-bus-breakout-board-include-teensy-4-0
 *
 * LIN-bus library must be installed first.
 * https://github.com/MarkusLange/Teensy_3.x_4.x_and_LC_LIN_Master
 *
 * NCV7430 RGB board:
 * https://www.skpang.co.uk/collections/breakout-boards/products/ncv7430-lin-bus-rgb-led-breakout-baord
 *
 */

#include "lin_bus.h"

// Create an IntervalTimer object
IntervalTimer myTimer;

int ledState = LOW;                // ledState used to set the LED
unsigned long interval = 200000;   // interval at which to blinkLED to run every 0.2 seconds

#define SET_LED_CONTROL 0x23
#define SET_LED_COLOUR  0x24

//LIN lin(&Serial3, 19200);
LIN lin;
int lin_cs = 32;

int led1 = 23;
int lin_fault = 28;
//                              Grp   Grp   Fade  Intense  G     R     B
uint8_t buffer_red[]   = {0xc0, 0x00, 0x00, 0x00, 0x31, 0x00, 0xff, 0x00};
uint8_t buffer_green[] = {0xc0, 0x00, 0x00, 0x00, 0x31, 0xff, 0x00, 0x00};
uint8_t buffer_blue[]  = {0xc0, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0xff};


void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(lin_fault,INPUT);
  pinMode(lin_cs, OUTPUT);
  digitalWrite(lin_cs, HIGH);
  digitalWrite(LED_BUILTIN, HIGH);
 
  lin.begin(&Serial3, 19200);

  delay(1000);
  pinMode(led1,OUTPUT);
    digitalWrite(LED_BUILTIN, LOW);

  Serial.begin(115200);
  Serial.print("NVC7430 RGB Demo");

  init_ncv7430();
  myTimer.begin(blinkLED, interval);

}
void loop() {
  // Red
  Serial.println("red");
  set_nvc7430_color(buffer_red);
  delay(500);
 
  // Green
  Serial.println("green");
  set_nvc7430_color(buffer_green);
  delay(500);
 
  // Blue
  Serial.println("blue");
  set_nvc7430_color(buffer_blue);
  delay(500);
}

void init_ncv7430(void) {
  uint8_t control_buffer[] = {0xc0, 0x00, 0x00, 0x7f};
 
  lin.order(SET_LED_CONTROL, control_buffer, 4);
}

void set_nvc7430_color(byte* message) {
  lin.order(SET_LED_COLOUR, message, 8);
}

void blinkLED() {
  ledState = !ledState;
 
  digitalWrite(LED_BUILTIN, ledState);
  digitalWrite(led1, ledState);
}

so I am wondering if we modify this code to try to get a response from the heater, do you see anything here that might be missing in the other code we were working on?

Once I read my oscilloscope manual, I can try that out on the other code.
 
Hi Jordan,

Does the unaltered "demo" sketch work as well? [ofcourse you need to change this LIN lin(&Serial3, 19200); and this int lin_cs = 32;].
I would like to stick to the original examples as close as possible. For example, keep the line LIN lin(&Serial3, 19200); where it is and don't use lin.begin(&Serial3, 19200); as it doesn't seem necessary.

On the logic analyzer all looks well:
1708157146223.png


What surprises me is that your sketch and the example sketch are communicating in "lin1x" mode [and apparently work]? On the NCV7430 website it states: "LIN Physical Layer according to LIN 2.1/ SAE J2602".

If the above is successful, would you mind running the "full status" sketch as well to make sure 2-way communication also works?

Unfortunately my ordered hardware is still stuck somewhere at customs after paying the import taxes...

Paul
 
Hi Paul. Thanks for the additional information and I will give the above a shot and see what happens.

Also, from the OpenInverter Board, I asked about the IF ELSE statements in the heater sketch and got this response if it helps with what we are trying to do:
In order to receive data, data has to be requested. The second "if" sends a request header to ID 22 if in read mode, else it sends power command to ID 21. The code alternates between read and send every 100ms. The first "if" checks if data is received on ID 22 and if it has prints out the data. I am not sure on the timing of adding status read, whether it can be immediately after the request for id 22 is sent or has to be every 100ms after request/data is sent to id 21 and 22. Somewhere you also need to add lin.order(23, 0, 0, lin2x); to request data, lin.response(23, data, 8, lin2x) to read data and then decode each of the bytes.
 
Paul, the sketch from GITHUB compiles but does not work because the teensy gets into a 10 second reboot loop and the amber light never blinks. SK PANG sent me this SKETCH that compiles and the Teensy blinks and the RGB board outputs Red Green and Blue.

Code:
/*
 *
 * Teensy 4.0 CAN FD and LIN-bus demo.
 *
 * For use with this board:
 * https://www.skpang.co.uk/products/teensy-4-0-can-fd-and-lin-bus-breakout-board-include-teensy-4-0
 *
 * LIN-bus library must be installed first.
 * https://github.com/MarkusLange/Teensy_3.x_4.x_and_LC_LIN_Master
 *
 * NCV7430 RGB board:
 * https://www.skpang.co.uk/collections/breakout-boards/products/ncv7430-lin-bus-rgb-led-breakout-baord
 *
 */

#include "lin_bus.h"
#include <FlexCAN_T4.h>
FlexCAN_T4FD<CAN3, RX_SIZE_256, TX_SIZE_16> FD;


// Create an IntervalTimer object
IntervalTimer myTimer;

int ledState = LOW;                // ledState used to set the LED
unsigned long interval = 200000;   // interval at which to blinkLED to run every 0.2 seconds

#define SET_LED_CONTROL 0x23
#define SET_LED_COLOUR  0x24

//LIN lin(&Serial3, 19200);
LIN lin;
int lin_cs = 32;

int led1 = 23;
int lin_fault = 28;
//                              Grp   Grp   Fade  Intense  G     R     B
uint8_t buffer_red[]   = {0xc0, 0x00, 0x00, 0x00, 0x31, 0x00, 0xff, 0x00};
uint8_t buffer_green[] = {0xc0, 0x00, 0x00, 0x00, 0x31, 0xff, 0x00, 0x00};
uint8_t buffer_blue[]  = {0xc0, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0xff};
uint8_t can_data;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(lin_fault,INPUT);
  pinMode(lin_cs, OUTPUT);
  digitalWrite(lin_cs, HIGH);
  digitalWrite(LED_BUILTIN, HIGH);
 
  lin.begin(&Serial3, 19200);

  delay(1000);
  pinMode(led1,OUTPUT);
    digitalWrite(LED_BUILTIN, LOW);

  Serial.begin(115200);
  Serial.print("NVC7430 RGB Demo");

  init_ncv7430();
  myTimer.begin(blinkLED, interval);

  FD.begin();
  CANFD_timings_t config;
  config.clock = CLK_24MHz;
  config.baudrate =   500000;     // 500kbps Nominal data rate
  config.baudrateFD = 2000000;    // 2000kpbs Data rate
  config.propdelay = 190;
  config.bus_length = 1;
  config.sample = 75;
  FD.setRegions(64);
  FD.setBaudRate(config);


  FD.setMBFilter(ACCEPT_ALL);
  FD.setMBFilter(MB13, 0x1);
  FD.setMBFilter(MB12, 0x1, 0x3);
  FD.setMBFilterRange(MB8, 0x1, 0x04);
  FD.enableMBInterrupt(MB8);
  FD.enableMBInterrupt(MB12);
  FD.enableMBInterrupt(MB13);
  FD.enhanceFilter(MB8);
  FD.enhanceFilter(MB10);
  FD.distribute();
  FD.mailboxStatus();

 
}
void sendframe()
{
  CANFD_message_t msg;
  msg.len = 64;           // Set frame length to 64 bytes
  msg.id = 0x321;
  msg.seq = 1;
  msg.buf[0] = can_data++;       
  msg.buf[1] = 1;
  msg.buf[2] = 2;
  msg.buf[3] = 3;
  msg.buf[4] = 4; 
  msg.buf[5] = 5;
  msg.buf[6] = 6;
  msg.buf[7] = 7;
  FD.write( msg);
}


void loop() {
  // Red
  Serial.println("red");
  set_nvc7430_color(buffer_red);
  delay(500);
 
  // Green
  Serial.println("green");
  set_nvc7430_color(buffer_green);
  delay(500);
 
  // Blue
  Serial.println("blue");
  set_nvc7430_color(buffer_blue);
  delay(500);
}

void init_ncv7430(void) {
  uint8_t control_buffer[] = {0xc0, 0x00, 0x00, 0x7f};
 
  lin.order(SET_LED_CONTROL, control_buffer, 4);
}

void set_nvc7430_color(byte* message) {
  lin.order(SET_LED_COLOUR, message, 8);
}

void blinkLED() {
  ledState = !ledState;
 
  digitalWrite(LED_BUILTIN, ledState);
  digitalWrite(led1, ledState);
  sendframe();
}

I asked SK PANG the the difference in the code that makes this work and he said:

The library has changed so the sketch needs to be updated.
 
Paul, on the "full status" Sketch, it was not working initially, but when I modified the code to the below, it worked and returned the following message:

NVC7430 Full Status DemoCRC: 1B
C0 FF F8 C0 F0 88 F0 FF

Code:
#include "lin_bus.h"

// Create an IntervalTimer object
IntervalTimer myTimer;

int ledState = LOW;                // ledState used to set the LED
unsigned long interval = 200000;   // interval at which to blinkLED to run every 0.2 seconds

#define SET_LED_CONTROL         0x23
#define DIAGNOSTIC_FRAME_MASTER 0x3c
#define DIAGNOSTIC_FRAME_SLAVE  0x3d

//LIN lin(&Serial3, 19200);
LIN lin;
int lin_cs = 32;

int led1 = 23;
int lin_fault = 28;

#define len 8
uint8_t lin_data[len];

void init_ncv7430(void) {
  uint8_t control_buffer[] = {0xc0, 0x00, 0x00, 0x7f};
 
  lin.order(SET_LED_CONTROL, control_buffer, 4);
}

void get_nvc7430_full_status(uint8_t* data, uint8_t lenght) {
  uint8_t rx_buffer[] = {0x80, 0x81, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff};
 
  lin.order(DIAGNOSTIC_FRAME_MASTER, rx_buffer, 8);

  Serial.print("CRC: ");
  Serial.println(lin.response(DIAGNOSTIC_FRAME_SLAVE, data, lenght),HEX);
}

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(lin_fault,INPUT);
  pinMode(lin_cs, OUTPUT);
  digitalWrite(lin_cs, HIGH);
  digitalWrite(LED_BUILTIN, HIGH);

  lin.begin(&Serial3, 19200);

  delay(1000);
  pinMode(led1,OUTPUT);
    digitalWrite(LED_BUILTIN, LOW);
 
  Serial.begin(115200);
  Serial.print("NVC7430 Full Status Demo");
 
  init_ncv7430();
  myTimer.begin(blinkLED, interval);
}

void loop() {
  get_nvc7430_full_status(lin_data, len);
  frame(lin_data, len);
  delay(3000);
}

void frame(uint8_t* data, uint8_t lenght) {
  for (int i=0; i<lenght; i++) {
    if (data[i] < 0x10)
      Serial.print("0");
    
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }
  Serial.println();
}

void blinkLED() {
  ledState = !ledState;
 
  digitalWrite(LED_BUILTIN, ledState);
  digitalWrite(led1, ledState);
}

I then went back and tried to only modify as little as possible of the FULL STATUS code to get the Teensy to work with the amber light blinking and a serial monitor response: I changed

LIN lin(&Serial1, 19200);

TO

LIN lin;///(&Serial1, 19200);

AND added

lin.begin(&Serial3, 19200);

into the VOID SETUP and it returned the following:

VC7430 Full Status DemoCRC: FFFFFFFF
00 58 18 00 20 08 00 00

Code:
#include "lin_bus.h"

// Create an IntervalTimer object
IntervalTimer myTimer;

int ledState = LOW;                // ledState used to set the LED
unsigned long interval = 200000;   // interval at which to blinkLED to run every 0.2 seconds

#define SET_LED_CONTROL         0x23
#define DIAGNOSTIC_FRAME_MASTER 0x3c
#define DIAGNOSTIC_FRAME_SLAVE  0x3d

int lin_cs = 23;

LIN lin;///(&Serial1, 19200);

#define len 8
uint8_t lin_data[len];

void init_ncv7430(void) {
  uint8_t control_buffer[] = {0xc0, 0x00, 0x00, 0x7f};
 
  lin.order(SET_LED_CONTROL, control_buffer, 4);
}

void get_nvc7430_full_status(uint8_t* data, uint8_t lenght) {
  uint8_t rx_buffer[] = {0x80, 0x81, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff};
 
  lin.order(DIAGNOSTIC_FRAME_MASTER, rx_buffer, 8);

  Serial.print("CRC: ");
  Serial.println(lin.response(DIAGNOSTIC_FRAME_SLAVE, data, lenght),HEX);
}

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(lin_cs, OUTPUT);
  digitalWrite(lin_cs, HIGH);
 
  Serial.begin(115200);
  Serial.print("NVC7430 Full Status Demo");

    lin.begin(&Serial3, 19200);
 
  init_ncv7430();
  myTimer.begin(blinkLED, interval);
}

void loop() {
  get_nvc7430_full_status(lin_data, len);
  frame(lin_data, len);
  delay(3000);
}

void frame(uint8_t* data, uint8_t lenght) {
  for (int i=0; i<lenght; i++) {
    if (data[i] < 0x10)
      Serial.print("0");
    
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }
  Serial.println();
}

void blinkLED() {
  ledState = !ledState;
 
  digitalWrite(LED_BUILTIN, ledState);
}
 
I went back to the first code in the above post for further testing and when I pull power from the board it goes to:

CRC: FFFFFFFF
00 58 18 00 20 08 00 00
but when I reapply power and LIN it goes to:
CRC: FB
C0 FF F8 D0 F0 88 00 FF
CRC: C
C0 FF F8 C0 F0 88 00 FF

FWIW
 
Allright, needed some time to digest all of this.
@skpang : do I understand correctly that the library has changed such that LIN lin(&Serial3, 19200); needs to be replaced by LIN lin; and lin.begin(&Serial3, 19200); needs to be added to setup()?
I don't understand this but if that is the way, I'm fine with it.

Curiously, before and after this change, my logic analyzer shows the same correct output with this simple sketch:
C++:
#include "lin_bus.h"

LIN lin; // LIN lin(&Serial3, 19200);

uint8_t message[] = { 0xAA };

void setup() {
  lin.begin(&Serial3, 19200);
}

void loop() {
  lin.order(35, message, 1, lin2x);
  delay(100);
}

DSView-240217-201553.png


Paul
 
Yes, those changes are correct. They are needed as Markus library has changed to a two step init.
 
Received my ordered hardware today. Both the LIN bus board and NCV7430 board work flawlessly with the Teensy LIN library.
Noticable is that the NCV7430 part claims to be "LIN Physical Layer according to LIN 2.1/ SAE J2602", but you have to stick to the LIN 1.3 "Standard Checksum", not the LIN2.x Enhanced Checksum.
Verified the outgoing and returned LIN data to be OK on the logic analyzer.

For reference, below are 2 working sketches. I used the NCV7430 register names from the datasheet.
Teensy4_NCV7430-RGB-demo:
C++:
#include "lin_bus.h"
#define ID_Set_LED_Control 0x23
#define ID_Set_Color 0x24

int linCS = 23;

uint8_t Set_LED_Control[] = { 0xC0, 0x00, 0x00, 0x7F };
uint8_t Set_Color_red[] = { 0xC0, 0x00, 0x00, 0x00, 0x31, 0x00, 0xFF, 0x00 };
uint8_t Set_Color_green[] = { 0xC0, 0x00, 0x00, 0x00, 0x31, 0xFF, 0x00, 0x00 };
uint8_t Set_Color_blue[] = { 0xC0, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0xFF };

LIN lin;

void setup() {
  pinMode(linCS, OUTPUT);
  digitalWrite(linCS, HIGH);

  Serial.begin(115200);
  Serial.println("NVC7430 RGB Demo");

  lin.begin(&Serial3, 19200, 13);
  lin.order(ID_Set_LED_Control, Set_LED_Control, 4);  // init NCV7430
}

void loop() {
  Serial.println("red");
  lin.order(ID_Set_Color, Set_Color_red, 8);
  delay(1000);

  Serial.println("green");
  lin.order(ID_Set_Color, Set_Color_green, 8);
  delay(1000);

  Serial.println("blue");
  lin.order(ID_Set_Color, Set_Color_blue, 8);
  delay(1000);
}
Teensy4_NCV7430-Get-Full-Status:
C++:
#include "lin_bus.h"

#define ID_Set_LED_Control 0x23
#define ID_Command_Get_Full_Status 0x3C
#define ID_Response_Get_Full_Status 0x3D

int linCS = 23;
uint8_t Command_Get_Full_Status[] = { 0x80, 0x81, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
uint8_t Response_Get_Full_Status[8];
uint8_t Set_LED_Control[] = { 0xC0, 0x00, 0x00, 0x7F };
uint8_t CRC;

LIN lin;

void setup() {
  pinMode(linCS, OUTPUT);
  digitalWrite(linCS, HIGH);

  Serial.begin(115200);
  Serial.println("NVC7430 Full Status Demo");

  lin.begin(&Serial3, 19200, 13);
  lin.order(ID_Set_LED_Control, Set_LED_Control, 4);  // init NCV7430
}

void loop() {
  lin.order(ID_Command_Get_Full_Status, Command_Get_Full_Status, 8);  // send command to read NCV7430 status

  CRC = lin.response(ID_Response_Get_Full_Status, Response_Get_Full_Status, 8);  // read NCV7430 status bytes
  Serial.print("CRC: ");
  Serial.println(CRC, HEX);

  for (int i = 0; i < 8; i++) {  // send all NCV7430 status bytes to serial monitor
    if (Response_Get_Full_Status[i] < 0x10) Serial.print("0");
    Serial.print(Response_Get_Full_Status[i], HEX);
    Serial.print(" ");
  }
  Serial.println();

  delay(3000);
}

Paul
 
Paul, thank you for getting this equipment and testing it.

When you run the heater code, would you see anything on the oscilloscope?
 
I may have found a bug in the CRC calculation and/or message data display in the LIN library.
With the code from Teensy4_NCV7430-Get-Full-Status in message #19 above, the serial monitor shows this:

1708604573953.png


However, my scope and logic analyzer show a different message and CRC:

SDS00003.png


DSView-240222-131010.png


The serial monitor shows CRC to be 0xFF while the scope and LA show CRC 0x1B.
Furthermore, on the serial monitor, the 8 byte return message data looks to be shifted left by 1 byte.
The data message should be C0 FF F8 C0 F0 88 F0 FF but the serial monitor shows FF F8 C0 F0 88 F0 FF 1B. That last byte is the CRC, while the first byte 0xCO has dropped.

I have been looking into lin_bus.cpp for half an hour now and I can't see where it's going wrong (yet).

Perhaps someone might want to have a look at the code? The problem is somewhere from line 451.

Thanks,
Paul
 
Code:
#if defined(__IMXRT1062__) // Teensy 4.0 & 4.1 clear Uart Buffer
  _stream->read();
  //Serial.println(_stream->read(),HEX);
#endif
A byte is thrown away at lines 462..465.
Might be the reason.
Uncomment the //Serial.println(_stream->read(),HEX); and see if the missing char is in fact this thrown away char.
 
Thanks for looking into it.
"A byte is thrown away at lines 462..465.". You are probably right, but I do not understand it?

If I uncomment Serial.println(_stream->read(),HEX);, the serial monitor shows:
17:03:57.890 -> 55
17:03:57.890 -> CRC: FF
17:03:57.890 -> F8 C0 F0 88 F0 FF 1B 00
17:04:00.886 -> 55
17:04:00.886 -> CRC: FF
17:04:00.886 -> F8 C0 F0 88 F0 FF 1B 00
That now printed byte 0x55 looks like the SYNC character. And the message data is shifted left another byte.

Paul
 
Not sure if this is any help, but the CPP file that was used on the STM32 HVH heater project with open inverter looks like this:

Code:
/*
 * This file is part of the stm32-... project.
 *
 * Copyright (C) 2021 Johannes Huebner <dev@johanneshuebner.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <libopencm3/stm32/dma.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/gpio.h>
#include "linbus.h"

#define HWINFO_ENTRIES (sizeof(hwInfo) / sizeof(struct HwInfo))

const LinBus::HwInfo LinBus::hwInfo[] =
{
   { USART1, DMA_CHANNEL4, DMA_CHANNEL5, GPIOA, GPIO_USART1_TX },
   { USART2, DMA_CHANNEL7, DMA_CHANNEL6, GPIOA, GPIO_USART2_TX },
   { USART3, DMA_CHANNEL2, DMA_CHANNEL3, GPIOB, GPIO_USART3_TX },
};


/** \brief Create a new LIN bus object and initialize USART, GPIO and DMA
 * \pre According USART, GPIO and DMA clocks must be enabled
 * \param usart USART base address
 * \param baudrate 9600 or 19200
 *
 */
LinBus::LinBus(uint32_t usart, int baudrate)
   : usart(usart)
{
   hw = hwInfo;

   for (uint32_t i = 0; i < HWINFO_ENTRIES; i++)
   {
      if (hw->usart == usart) break;
      hw++;
   }

   gpio_set_mode(hw->port, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, hw->pin);

   usart_set_baudrate(usart, baudrate);
   usart_set_databits(usart, 8);
   usart_set_stopbits(usart, USART_STOPBITS_1);
   usart_set_mode(usart, USART_MODE_TX_RX);
   usart_set_parity(usart, USART_PARITY_NONE);
   usart_set_flow_control(usart, USART_FLOWCONTROL_NONE);
   USART_CR2(usart) |= USART_CR2_LINEN;
   usart_enable_tx_dma(usart);
   usart_enable_rx_dma(usart);

   dma_channel_reset(DMA1, hw->dmatx);
   dma_set_read_from_memory(DMA1, hw->dmatx);
   dma_set_peripheral_address(DMA1, hw->dmatx, (uint32_t)&USART_DR(usart));
   dma_set_memory_address(DMA1, hw->dmatx, (uint32_t)sendBuffer);
   dma_set_peripheral_size(DMA1, hw->dmatx, DMA_CCR_PSIZE_8BIT);
   dma_set_memory_size(DMA1, hw->dmatx, DMA_CCR_MSIZE_8BIT);
   dma_enable_memory_increment_mode(DMA1, hw->dmatx);

   dma_channel_reset(DMA1, hw->dmarx);
   dma_set_peripheral_address(DMA1, hw->dmarx, (uint32_t)&USART_DR(usart));
   dma_set_peripheral_size(DMA1, hw->dmarx, DMA_CCR_PSIZE_8BIT);
   dma_set_memory_size(DMA1, hw->dmarx, DMA_CCR_MSIZE_8BIT);
   dma_enable_memory_increment_mode(DMA1, hw->dmarx);

   usart_enable(usart);
}

/** \brief Send data on LIN bus
 *
 * \param id feature ID
 * \param data payload data, if any
 * \param len length of payload, if any
 *
 */
void LinBus::Request(uint8_t id, uint8_t* data, uint8_t len)
{
   int sendLen = len == 0 ? 2 : len + 3;

   if (len > 8) return;

   dma_disable_channel(DMA1, hw->dmatx);
   dma_set_number_of_data(DMA1, hw->dmatx, sendLen);
   dma_disable_channel(DMA1, hw->dmarx);
   dma_set_memory_address(DMA1, hw->dmarx, (uint32_t)recvBuffer);
   dma_set_number_of_data(DMA1, hw->dmarx, sizeof(recvBuffer));

   sendBuffer[0] = 0x55; //Sync
   sendBuffer[1] = Parity(id);

   for (uint8_t i = 0; i < len; i++)
      sendBuffer[i + 2] = data[i];

   sendBuffer[len + 2] = Checksum(sendBuffer[1], data, len);

   dma_clear_interrupt_flags(DMA1, hw->dmatx, DMA_TCIF);

   USART_CR1(usart) |= USART_CR1_SBK;
   dma_enable_channel(DMA1, hw->dmatx);
   dma_enable_channel(DMA1, hw->dmarx);
}

/** \brief Check whether we received valid data with given PID and length
 *
 * \param pid Feature ID to check for
 * \param requiredLen Length of data we expect
 * \return true if data with given properties was received
 *
 */
bool LinBus::HasReceived(uint8_t id, uint8_t requiredLen)
{
   int numRcvd = dma_get_number_of_data(DMA1, hw->dmarx);
   int receiveIdx = sizeof(recvBuffer) - numRcvd;

   if (requiredLen > 8) return false;

   uint8_t pid = Parity(id);

   if (receiveIdx == (requiredLen + payloadIndex + 1) && recvBuffer[pidIndex] == pid)
   {
      uint8_t checksum = Checksum(recvBuffer[pidIndex], &recvBuffer[payloadIndex], requiredLen);

      return checksum == recvBuffer[requiredLen + payloadIndex];
   }

   return false;
}

/** \brief Calculate LIN checksum
 *
 * \param pid ID with parity
 * \param data uint8_t*
 * \param len int
 * \return checksum
 *
 */
uint8_t LinBus::Checksum(uint8_t pid, uint8_t* data, int len)
{
   uint8_t checksum = pid;

   for (int i = 0; i < len; i++)
   {
      uint16_t tmp = (uint16_t)checksum + (uint16_t)data[i];
      if (tmp > 256) tmp -= 255;
      checksum = tmp;
   }
   return checksum ^ 0xff;
}

uint8_t LinBus::Parity(uint8_t id)
{
   bool p1 = !(((id & 0x2) > 0) ^ ((id & 0x8) > 0) ^ ((id & 0x10) > 0) ^ ((id & 0x20) > 0));
   bool p0 = ((id & 0x1) > 0) ^ ((id & 0x2) > 0) ^ ((id & 0x4) > 0) ^ ((id & 0x10) > 0);

   return id | p1 << 7 | p0 << 6;
}
 
I am running the heater Sketch you built Paul with the CPP file uncommented and I am getting 55 as a response now instead of FFFFFFFF

1708619502773.png

oscilloscope responses with HV on and 12V on.

1708619599387.png


1708619620243.png
 
Back
Top