Author Topic: Need help powering up a car radio (ntg4 rer / MYGIG Uconnect 730N) on the bench  (Read 8434 times)

0 Members and 1 Guest are viewing this topic.

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
I have having trouble getting a ntg4 rer / MYGIG Uconnect 730N radio powered up on the bench.  I have found that it only has 12V and GND connections on the plug on the back (no ACC connection) and when I apply power to them it briefly spikes as the caps charge and then settles down to 0.30ma or so of draw.  I have found that if I press the disk eject button I get a quick spike of current to round 700 to 900ma and that is it.  I have done some research online and have found that this unit needs a Can bus signal to turn on and stay powered up.  The only CAN bus hardware I have is a STM32 blue pill board, a clone CAN bus transceiver chip (model VC1040 clone of TJA1040) from a partially broken OBDII dongle (damaged the main micro chip when trying to mod it for additional protocol support) and a Android Auto head unit made by XY Auto model YT9216-ver0.1 with a MCU version of 3.1.  I have found this software for the STM32 chip: https://github.com/nopnop2002/Arduino-STM32-CAN but am unsure if it is what I need to make this work.  The Android Auto unit clams to support CAN bus with a breakout box which I am going to guess is just a transceiver chip.  There is a Can bus information section in the service menu that I may be able to use but it could just be for monitoring the Can bus messages and not for sending them.  The issue is the radio is intermittently powering on and I want to rule out the car as the cause of the issue.
« Last Edit: May 09, 2021, 09:24:35 pm by poot36 »
 

Offline kripton2035

  • Super Contributor
  • ***
  • Posts: 2587
  • Country: fr
    • kripton2035 schematics repository
well wouldn't it easier to try the radio in another car ?
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
I do not have access to another car that would support this radio sadly.  And even if I did if there is a fault in the radio it may damage a working car and I would not want to risk that.
 

Offline dc101

  • Regular Contributor
  • *
  • Posts: 220
  • Country: us
I would buy a Raspberry Pi CAN bus shield, there are several different versions and plenty of software available for them. Much easier to run command-line Linux utils to capture and playback data then messing around with some microcontroller. Some co-workers of mine have successfully used can shields in the past to do exactly what you're trying to do.

For software something like this might work https://github.com/linux-can/can-utils  There are dozens of other software packages out there that can do can bus capture and replay, you'll just have to do a bit of google work to make sure the can bus shield you buy is supported by the can bus capture/replay software you intend to use.

You'll also need to do some research to determine what bus the radio is on so you know which bus you need to monitor. Most cars have at least 2 separate can buses, some newer cars have 3 or 4, but typically there is at least a low-speed and a high-speed can bus. Most non-safety traffic (like the radio) will be on the low-speed bus. For my Ford Fusion, the can bus is documented in the wiring diagram manual. I was able to find a few can bus schematics on line, but ultimately I just ended up buying a used manual off eBay for pretty cheap.

Hook your rpi can bus monitor to your vehicle, take a few captures of turning the radio on (don't record the power-off). Then you should be able to take those captures and play them back with the shield connected to your radio. The good news is that since you're only replaying the data to your radio, it is safe to replay the entire capture without having to try and isolate the specific power-on message. The other can bus traffic will be ignored by the radio. If you were to replay the traffic into your car's can bus while the car was on, well then that might cause some issues depending on the traffic and how sensitive your car is.
 
The following users thanked this post: edavid

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
I am really trying to avoid buying more stuff that I am going to only use once.  The radio came from a coworker that found out that I know stuff about electronics and I thought it would be a easy thing to fix.  How wrong I was.  I do not have access to the car.   The coworker does have a multimeter and I have instructed them to measure the voltage on the Can bus lines that are on the plug to the radio so hopefully they will find an issue there.  I am just wondering if I can make it work with the tools I have right now.  Still trying to figure out how to get the USB serial port working on the blue pill board after I have flashed it, it keeps coming up as a Maple DFU device and not as a com port.  I have it flashed with this bootloader: https://github.com/rogerclarkmelbourne/STM32duino-bootloader/blob/master/binaries/generic_boot20_pc13.bin and am using this: https://github.com/stm32duino/BoardManagerFiles/raw/master/package_stmicroelectronics_index.json for the board support.  Not sure if that is a compatible combination or not as this page: https://github.com/nopnop2002/Arduino-STM32-CAN mentions that it will not work with the board support for the bootloader I have on the chip but makes no mention of if I can use the bootloader from there or not.  I have enabled the USB CDC mode in the tools menu of the Arduino program and this has made an unknown USB device show up in device manager.
« Last Edit: May 09, 2021, 11:14:54 pm by poot36 »
 

Offline dc101

  • Regular Contributor
  • *
  • Posts: 220
  • Country: us
Ignoring the can bus hardware for now, without access to the vehicle it may be pretty hard to determine the proper message to turn on the radio. Each can bus frame has an ID that indicates the vehicle sub component and a data section that usually contains values for several different fields. For example, the engine ecu might be ID 123, followed by a data field that might contain the value for the air flow sensor, air temp, coolant temp etc... all within a single 64-bit value.

I'm sure there are other ways, but it seems like there are a couple of options. Research the vehicle type the radio belongs to and determine if anyone has previously identified the radio can bus messages. Check on vehicle forums to see if someone with the same vehicle could do can bus captures of the radio powering on. Less likely, but still worth looking into, ask a dealer if they can provide you with the information. Or you can try and fuzz the can bus by generating packets with random ids and data until you find something that works.

IMO I would be more focused on trying to determine what the can bus message is, or how to obtain a can bus packet from the appropriate vehicle, than the actual hardware to generate can bus messages. The hardware part has been done at great length and there is a plethora of information available on the topic, so once you know how to activate the radio, then just like you mentioned it becomes a decision about how much your time is worth as far as which hardware solution you decide to go with.

Here's some sample data that I pulled from a log for my old Mazda, I'm just filtering on one random ID, but it's just to give you an idea of what real can traffic looks like. So the challenge with reversing can is to determine which one of those 8 bytes in the data field are related to each other. Are they a single value, are there multiple values etc...
ID:0x160:STD:DLC:8:DATA:0x0A:0xF5:0x00:0x00:0x04:0x04:0x5D:0x00:
ID:0x160:STD:DLC:8:DATA:0x0A:0xF5:0x00:0x00:0x04:0x04:0x5D:0x00:
ID:0x160:STD:DLC:8:DATA:0x0A:0xF3:0x00:0x00:0x04:0x03:0x5D:0x00:
ID:0x160:STD:DLC:8:DATA:0x0A:0xF3:0x00:0x00:0x04:0x03:0x5D:0x00:
ID:0x160:STD:DLC:8:DATA:0x0A:0xF2:0x00:0x00:0x04:0x02:0x5D:0x00:
ID:0x160:STD:DLC:8:DATA:0x0A:0xF2:0x00:0x00:0x04:0x02:0x5D:0x00:
ID:0x160:STD:DLC:8:DATA:0x0A:0xF0:0x00:0x00:0x03:0xFF:0x5D:0x00:
ID:0x160:STD:DLC:8:DATA:0x0A:0xF0:0x00:0x00:0x03:0xFF:0x5D:0x00:
ID:0x160:STD:DLC:8:DATA:0x0A:0xEF:0x00:0x00:0x04:0x00:0x5D:0x00:
ID:0x160:STD:DLC:8:DATA:0x0A:0xEF:0x00:0x00:0x04:0x00:0x5D:0x00:
ID:0x160:STD:DLC:8:DATA:0x0A:0xEF:0x00:0x00:0x04:0x00:0x5D:0x00:
ID:0x160:STD:DLC:8:DATA:0x0A:0xED:0x00:0x00:0x03:0xFC:0x5D:0x00:
ID:0x160:STD:DLC:8:DATA:0x0A:0xEB:0x00:0x00:0x03:0xF6:0x5D:0x00:
ID:0x160:STD:DLC:8:DATA:0x0A:0xEB:0x00:0x00:0x03:0xF6:0x5D:0x00:
ID:0x160:STD:DLC:8:DATA:0x0A:0xE9:0x00:0x00:0x03:0xF4:0x5D:0x00:
ID:0x160:STD:DLC:8:DATA:0x0A:0xE7:0x00:0x00:0x03:0xF3:0x5D:0x00:
ID:0x160:STD:DLC:8:DATA:0x0A:0xE7:0x00:0x00:0x03:0xF3:0x5D:0x00:
ID:0x160:STD:DLC:8:DATA:0x0A:0xE7:0x00:0x00:0x03:0xF3:0x5D:0x00:
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
I think I found the commands I need to send here: http://canhack.org/board/viewtopic.php?f=1&t=161&p=822&hilit=bench#p822  Would have to get the hardware and software setup to test them out though.
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
I am getting closer, with this code I can get the radio to continually draw 700ma to 1.3A of power but not fully turn on.  I am just spamming it right now though.

void loop() {
  CAN_msg_t CAN_TX_msg;
  CAN_msg_t CAN_RX_msg;

  CAN_TX_msg.data[0] = 0x00;
  CAN_TX_msg.data[1] = 0x01;
  CAN_TX_msg.data[2] = 0x02;
  CAN_TX_msg.data[3] = 0x03;
  CAN_TX_msg.data[4] = 0x04;
  CAN_TX_msg.data[5] = 0x05;
  CAN_TX_msg.data[6] = 0x06;
  CAN_TX_msg.data[7] = 0x07;
  CAN_TX_msg.len = frameLength;

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    if ( ( counter % 2) == 0) {
      CAN_TX_msg.type = DATA_FRAME;
      if (CAN_TX_msg.len == 0) CAN_TX_msg.type = REMOTE_FRAME;
      CAN_TX_msg.format = STANDARD_FORMAT;
      CAN_TX_msg.id = 0x000;
    } else {
      CAN_TX_msg.type = DATA_FRAME;
      if (CAN_TX_msg.len == 0) CAN_TX_msg.type = REMOTE_FRAME;
      CAN_TX_msg.format = STANDARD_FORMAT;
      CAN_TX_msg.id = 0x400;
    }
    CANSend(&CAN_TX_msg);
    frameLength++;
    if (frameLength == 9) frameLength = 0;
    counter++;
  }

I do not have a termination resistor in the circuit could that be the issue?  Or do I have to figure out the code more so that I can send a repeating sequence of the same code instead of a incrementing data packet?
 

Offline dc101

  • Regular Contributor
  • *
  • Posts: 220
  • Country: us
Is that code you found on the can bus forum for the same model vehicle/radio as the one you have, if not I would guess that it's most likely not the correct code/data required to turn the radio on. The increase in current is probably a good sign though since it seems like the radio is monitoring the bus looking for relevant codes?

 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
This is slightly modified code from a example of how to use Can bus on the STM32f103 chip from here: https://github.com/nopnop2002/Arduino-STM32-CAN  as you can see from the screenshots it is just an incrementing code.  I have removed the extended ID section and replaced it with another standard ID section.  I am sending ID 400 and ID 000 alternating with incrementing data.  As I am not a coder I don't really understand how I would change this to just repeat one code until power off.
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
I got the serial output redirected to Serial 3 on the STM32 as the USB USART does not show up correctly (device manager shows unknown device) and I have attached to this post the log file of the output as it is to long for this forum.  I think it is looping.
 

Offline dc101

  • Regular Contributor
  • *
  • Posts: 220
  • Country: us
That's understandable, what I'm asking is, if you know for a fact that ID 400 is what your radio is expecting to receive? Have you found any documentation for your radio/vehicle that indicates this is the ID of the radio? Once you determine the ID, then you can move on to trying to determine what the data sent along with the ID is supposed to look like.

This is slightly modified code from a example of how to use Can bus on the STM32f103 chip from here: https://github.com/nopnop2002/Arduino-STM32-CAN  as you can see from the screenshots it is just an incrementing code.  I have removed the extended ID section and replaced it with another standard ID section.  I am sending ID 400 and ID 000 alternating with incrementing data.  As I am not a coder I don't really understand how I would change this to just repeat one code until power off.
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
I found that out from this other forum post from here http://canhack.org/board/viewtopic.php?f=1&t=161&p=822&hilit=bench#p822:

    stephan wrote:
    I did some direct discussion with colletjr in between and I think it's time to post some results to all involved here.
    With help from colletjr I could start the radio. And in further process I found out that a '000 63 00 00 00 00 00' which is sent at least every 800ms is fully sufficient to let the radio stay alive. It's only the wiring which is of influence on the actions happening there. Colletjr is right to use a short cable (twisted or non-twisted) without resistor-termination; ground should be connected. Listening to the communication of a radio with an extra can-controller (Atmel At90Can128 which sends the wake-ups e.g. every 500ms) shows that any change of the wiring leads to a missing acknowledge which is expected to be sent by the radio. In this case it seems that the radio is receiving the signal and reacts to this by turning on for a second. But as no acknowledge is received by the sender (the connected controller) this one repeats the wake-up signal permanently as fast as it can. All together results in a cycling wake-up of the radio for a second an a pause of approx. 4 seconds.
    To complete some data: The radio I played with is a 2004 with integrated mp3-6CD-changer with 2 big adjusting knobs from a 2005 Chrysler 300c.

    Hope this helps someone.
    If anybody knows the code for switching on the lights -please post. The '1C8 13...' or '1C8 03...' from the can-logger is not working.

You have to send any 4xx ID to the radio. For example..

MyID = 0x415; // this can be any number between 0x400 to 0x4ff except for 0x416 and as long as no other modules are on the bus.
TX_Data[0] = 0xfd;
TX_Data[1] = 0x16; // radio id 0x416
TX_Data[2] = 0x3f;
TX_Data[3] = 0xff;
TX_Data[4] = 0xff;
TX_Data[5] = 0xff;
TX_Data[6] = 0xff;
TX_Data[7] = 0xff;
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
Well I just found this image that I have attached to this post from this forum http://canhack.org/board/viewtopic.php?f=2&t=1173&sid=9b615b8b1be4d52afb62f2addbd51f19:

Looks like I need to figure out how to send a 6 bit code along with a 8 bit code and the radio should turn on (I hope).  Not sure how to do this though.

Here is the code I am using:
Code: [Select]
#define DEBUG 0

//                      RX    TX
HardwareSerial Serial3(PA10, PA9);

/* Symbolic names for bit rate of CAN message                                */
typedef enum {CAN_50KBPS, CAN_100KBPS, CAN_125KBPS, CAN_250KBPS, CAN_500KBPS, CAN_1000KBPS} BITRATE;

/* Symbolic names for formats of CAN message                                 */
typedef enum {STANDARD_FORMAT = 0, EXTENDED_FORMAT} CAN_FORMAT;

/* Symbolic names for type of CAN message                                    */
typedef enum {DATA_FRAME = 0, REMOTE_FRAME}         CAN_FRAME;


typedef struct
{
  uint32_t id;        /* 29 bit identifier                               */
  uint8_t  data[8];   /* Data field                                      */
  uint8_t  len;       /* Length of data field in bytes                   */
  uint8_t  ch;        /* Object channel(Not use)                         */
  uint8_t  format;    /* 0 - STANDARD, 1- EXTENDED IDENTIFIER            */
  uint8_t  type;      /* 0 - DATA FRAME, 1 - REMOTE FRAME                */
} CAN_msg_t;

typedef const struct
{
  uint8_t TS2;
  uint8_t TS1;
  uint8_t BRP;
} CAN_bit_timing_config_t;

CAN_bit_timing_config_t can_configs[6] = {{2, 13, 45}, {2, 15, 20}, {2, 13, 18}, {2, 13, 9}, {2, 15, 4}, {2, 15, 2}};

/**
 * Print registers.
*/
void printRegister(char * buf, uint32_t reg) {
  if (DEBUG == 0) return;
  Serial3.print(buf);
  Serial3.print(reg, HEX);
  Serial3.println();
}

/**
 * Initializes the CAN filter registers.
 *
 * @preconditions   - This register can be written only when the filter initialization mode is set (FINIT=1) in the CAN_FMR register.
 * @params: index   - Specified filter index. index 27:14 are available in connectivity line devices only.
 * @params: scale   - Select filter scale.
 *                    0: Dual 16-bit scale configuration
 *                    1: Single 32-bit scale configuration
 * @params: mode    - Select filter mode.
 *                    0: Two 32-bit registers of filter bank x are in Identifier Mask mode
 *                    1: Two 32-bit registers of filter bank x are in Identifier List mode
 * @params: fifo    - Select filter assigned.
 *                    0: Filter assigned to FIFO 0
 *                    1: Filter assigned to FIFO 1
 * @params: bank1   - Filter bank register 1
 * @params: bank2   - Filter bank register 2
 *
 */
void CANSetFilter(uint8_t index, uint8_t scale, uint8_t mode, uint8_t fifo, uint32_t bank1, uint32_t bank2) {
  if (index > 27) return;

  CAN1->FA1R &= ~(0x1UL<<index);               // Deactivate filter

  if (scale == 0) {
    CAN1->FS1R &= ~(0x1UL<<index);             // Set filter to Dual 16-bit scale configuration
  } else {
    CAN1->FS1R |= (0x1UL<<index);              // Set filter to single 32 bit configuration
  }
    if (mode == 0) {
    CAN1->FM1R &= ~(0x1UL<<index);             // Set filter to Mask mode
  } else {
    CAN1->FM1R |= (0x1UL<<index);              // Set filter to List mode
  }

  if (fifo == 0) {
    CAN1->FFA1R &= ~(0x1UL<<index);            // Set filter assigned to FIFO 0
  } else {
    CAN1->FFA1R |= (0x1UL<<index);             // Set filter assigned to FIFO 1
  }

  CAN1->sFilterRegister[index].FR1 = bank1;    // Set filter bank registers1
  CAN1->sFilterRegister[index].FR2 = bank2;    // Set filter bank registers2

  CAN1->FA1R |= (0x1UL<<index);                // Activate filter

}

/**
 * Initializes the CAN controller with specified bit rate.
 *
 * @params: bitrate - Specified bitrate. If this value is not one of the defined constants, bit rate will be defaulted to 125KBS
 * @params: remap   - Select CAN port.
 *                    =0:CAN_RX mapped to PA11, CAN_TX mapped to PA12
 *                    =1:Not used
 *                    =2:CAN_RX mapped to PB8, CAN_TX mapped to PB9 (not available on 36-pin package)
 *                    =3:CAN_RX mapped to PD0, CAN_TX mapped to PD1 (available on 100-pin and 144-pin package)
 *
 */
bool CANInit(BITRATE bitrate, int remap)
{
  // Reference manual
  // [url=https://www.st.com/content/ccc/resource/technical/document/reference_manual/59/b9/ba/7f/11/af/43/d5/CD00171190.pdf/files/CD00171190.pdf/jcr:content/translations/en.CD00171190.pdf]https://www.st.com/content/ccc/resource/technical/document/reference_manual/59/b9/ba/7f/11/af/43/d5/CD00171190.pdf/files/CD00171190.pdf/jcr:content/translations/en.CD00171190.pdf[/url]

  RCC->APB1ENR |= 0x2000000UL;       // Enable CAN clock
  RCC->APB2ENR |= 0x1UL;             // Enable AFIO clock
  AFIO->MAPR   &= 0xFFFF9FFF;        // reset CAN remap
                                     // CAN_RX mapped to PA11, CAN_TX mapped to PA12

  if (remap == 0) {
    RCC->APB2ENR |= 0x4UL;           // Enable GPIOA clock
    GPIOA->CRH   &= ~(0xFF000UL);    // Configure PA12(0b0000) and PA11(0b0000)
                                     // 0b0000
                                     //   MODE=00(Input mode)
                                     //   CNF=00(Analog mode)

    GPIOA->CRH   |= 0xB8FFFUL;       // Configure PA12(0b1011) and PA11(0b1000)
                                     // 0b1011
                                     //   MODE=11(Output mode, max speed 50 MHz)
                                     //   CNF=10(Alternate function output Push-pull
                                     // 0b1000
                                     //   MODE=00(Input mode)
                                     //   CNF=10(Input with pull-up / pull-down)
                                     
    GPIOA->ODR |= 0x1UL << 12;       // PA12 Upll-up
   
  }
                               
  if (remap == 2) {
    AFIO->MAPR   |= 0x00004000;      // set CAN remap
                                     // CAN_RX mapped to PB8, CAN_TX mapped to PB9 (not available on 36-pin package)

    RCC->APB2ENR |= 0x8UL;           // Enable GPIOB clock
    GPIOB->CRH   &= ~(0xFFUL);       // Configure PB9(0b0000) and PB8(0b0000)
                                     // 0b0000
                                     //   MODE=00(Input mode)
                                     //   CNF=00(Analog mode)

    GPIOB->CRH   |= 0xB8UL;          // Configure PB9(0b1011) and PB8(0b1000)
                                     // 0b1011
                                     //   MODE=11(Output mode, max speed 50 MHz)
                                     //   CNF=10(Alternate function output Push-pull
                                     // 0b1000
                                     //   MODE=00(Input mode)
                                     //   CNF=10(Input with pull-up / pull-down)
                                     
    GPIOB->ODR |= 0x1UL << 8;        // PB8 Upll-up
  }
 
  if (remap == 3) {
    AFIO->MAPR   |= 0x00005000;      // set CAN remap
                                     // CAN_RX mapped to PD0, CAN_TX mapped to PD1 (available on 100-pin and 144-pin package)

    RCC->APB2ENR |= 0x20UL;          // Enable GPIOD clock
    GPIOD->CRL   &= ~(0xFFUL);       // Configure PD1(0b0000) and PD0(0b0000)
                                     // 0b0000
                                     //   MODE=00(Input mode)
                                     //   CNF=00(Analog mode)

    GPIOD->CRH   |= 0xB8UL;          // Configure PD1(0b1011) and PD0(0b1000)
                                     // 0b1000
                                     //   MODE=00(Input mode)
                                     //   CNF=10(Input with pull-up / pull-down)
                                     // 0b1011
                                     //   MODE=11(Output mode, max speed 50 MHz)
                                     //   CNF=10(Alternate function output Push-pull
                                     
    GPIOD->ODR |= 0x1UL << 0;        // PD0 Upll-up
  }

  CAN1->MCR |= 0x1UL;                   // Require CAN1 to Initialization mode
  while (!(CAN1->MSR & 0x1UL));         // Wait for Initialization mode

  //CAN1->MCR = 0x51UL;                 // Hardware initialization(No automatic retransmission)
  CAN1->MCR = 0x41UL;                   // Hardware initialization(With automatic retransmission)
   
  // Set bit rates
  CAN1->BTR &= ~(((0x03) << 24) | ((0x07) << 20) | ((0x0F) << 16) | (0x1FF));
  CAN1->BTR |=  (((can_configs[bitrate].TS2-1) & 0x07) << 20) | (((can_configs[bitrate].TS1-1) & 0x0F) << 16) | ((can_configs[bitrate].BRP-1) & 0x1FF);

  // Configure Filters to default values
  CAN1->FMR  |=   0x1UL;                // Set to filter initialization mode
  CAN1->FMR  &= 0xFFFFC0FF;             // Clear CAN2 start bank

  // bxCAN has 28 filters.
  // These filters are used for both CAN1 and CAN2.
  // STM32F103 has only CAN1, so all 28 are used for CAN1
  CAN1->FMR  |= 0x1C << 8;              // Assign all filters to CAN1

  // Set fileter 0
  // Single 32-bit scale configuration
  // Two 32-bit registers of filter bank x are in Identifier Mask mode
  // Filter assigned to FIFO 0
  // Filter bank register to all 0
  CANSetFilter(0, 1, 0, 0, 0x0UL, 0x0UL);
 
  CAN1->FMR   &= ~(0x1UL);              // Deactivate initialization mode

  uint16_t TimeoutMilliseconds = 1000;
  bool can1 = false;
  CAN1->MCR   &= ~(0x1UL);              // Require CAN1 to normal mode

  // Wait for normal mode
  // If the connection is not correct, it will not return to normal mode.
  for (uint16_t wait_ack = 0; wait_ack < TimeoutMilliseconds; wait_ack++) {
    if ((CAN1->MSR & 0x1UL) == 0) {
      can1 = true;
      break;
    }
    delayMicroseconds(1000);
  }
  //Serial3.print("can1=");
  //Serial3.println(can1);
  if (can1) {
    Serial3.println("CAN1 initialize ok");
  } else {
    Serial3.println("CAN1 initialize fail!!");
    return false;
  }
  return true;
}


#define STM32_CAN_TIR_TXRQ              (1U << 0U)  // Bit 0: Transmit Mailbox Request
#define STM32_CAN_RIR_RTR               (1U << 1U)  // Bit 1: Remote Transmission Request
#define STM32_CAN_RIR_IDE               (1U << 2U)  // Bit 2: Identifier Extension
#define STM32_CAN_TIR_RTR               (1U << 1U)  // Bit 1: Remote Transmission Request
#define STM32_CAN_TIR_IDE               (1U << 2U)  // Bit 2: Identifier Extension

#define CAN_EXT_ID_MASK                 0x1FFFFFFFU
#define CAN_STD_ID_MASK                 0x000007FFU
 
/**
 * Decodes CAN messages from the data registers and populates a
 * CAN message struct with the data fields.
 *
 * @preconditions A valid CAN message is received
 * @params CAN_rx_msg - CAN message structure for reception
 *
 */
void CANReceive(CAN_msg_t* CAN_rx_msg)
{
  uint32_t id = CAN1->sFIFOMailBox[0].RIR;
  if ((id & STM32_CAN_RIR_IDE) == 0) { // Standard frame format
      CAN_rx_msg->format = STANDARD_FORMAT;;
      CAN_rx_msg->id = (CAN_STD_ID_MASK & (id >> 21U));
  }
  else {                               // Extended frame format
      CAN_rx_msg->format = EXTENDED_FORMAT;;
      CAN_rx_msg->id = (CAN_EXT_ID_MASK & (id >> 3U));
  }

  if ((id & STM32_CAN_RIR_RTR) == 0) { // Data frame
      CAN_rx_msg->type = DATA_FRAME;
  }
  else {                               // Remote frame
      CAN_rx_msg->type = REMOTE_FRAME;
  }

 
  CAN_rx_msg->len = (CAN1->sFIFOMailBox[0].RDTR) & 0xFUL;
 
  CAN_rx_msg->data[0] = 0xFFUL &  CAN1->sFIFOMailBox[0].RDLR;
  CAN_rx_msg->data[1] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDLR >> 8);
  CAN_rx_msg->data[2] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDLR >> 16);
  CAN_rx_msg->data[3] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDLR >> 24);
  CAN_rx_msg->data[4] = 0xFFUL &  CAN1->sFIFOMailBox[0].RDHR;
  CAN_rx_msg->data[5] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDHR >> 8);
  CAN_rx_msg->data[6] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDHR >> 16);
  CAN_rx_msg->data[7] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDHR >> 24);

  // Release FIFO 0 output mailbox.
  // Make the next incoming message available.
  CAN1->RF0R |= 0x20UL;
}
 
/**
 * Encodes CAN messages using the CAN message struct and populates the
 * data registers with the sent.
 *
 * @params CAN_tx_msg - CAN message structure for transmission
 *
 */
void CANSend(CAN_msg_t* CAN_tx_msg)
{
  volatile int count = 0;

  uint32_t out = 0;
  if (CAN_tx_msg->format == EXTENDED_FORMAT) { // Extended frame format
      out = ((CAN_tx_msg->id & CAN_EXT_ID_MASK) << 3U) | STM32_CAN_TIR_IDE;
  }
  else {                                       // Standard frame format
      out = ((CAN_tx_msg->id & CAN_STD_ID_MASK) << 21U);
  }

  // Remote frame
  if (CAN_tx_msg->type == REMOTE_FRAME) {
      out |= STM32_CAN_TIR_RTR;
  }

  CAN1->sTxMailBox[0].TDTR &= ~(0xF);
  CAN1->sTxMailBox[0].TDTR |= CAN_tx_msg->len & 0xFUL;
 
  CAN1->sTxMailBox[0].TDLR  = (((uint32_t) CAN_tx_msg->data[3] << 24) |
                               ((uint32_t) CAN_tx_msg->data[2] << 16) |
                               ((uint32_t) CAN_tx_msg->data[1] <<  8) |
                               ((uint32_t) CAN_tx_msg->data[0]      ));
  CAN1->sTxMailBox[0].TDHR  = (((uint32_t) CAN_tx_msg->data[7] << 24) |
                               ((uint32_t) CAN_tx_msg->data[6] << 16) |
                               ((uint32_t) CAN_tx_msg->data[5] <<  8) |
                               ((uint32_t) CAN_tx_msg->data[4]      ));

  // Send Go
  CAN1->sTxMailBox[0].TIR = out | STM32_CAN_TIR_TXRQ;

  // Wait until the mailbox is empty
  while(CAN1->sTxMailBox[0].TIR & 0x1UL && count++ < 1000000);

  // The mailbox don't becomes empty while loop
  if (CAN1->sTxMailBox[0].TIR & 0x1UL) {
    Serial3.println("Send Fail");
    Serial3.println(CAN1->ESR);
    Serial3.println(CAN1->MSR);
    Serial3.println(CAN1->TSR);
  }
}

 /**
 * Returns whether there are CAN messages available.
 *
 * @returns If pending CAN messages are in the CAN controller
 *
 */
uint8_t CANMsgAvail(void)
{
  // Check for pending FIFO 0 messages
  return CAN1->RF0R & 0x3UL;
}


uint8_t counter = 0;
uint8_t frameLength = 0;
unsigned long previousMillis = 0;     // stores last time output was updated
const long interval = 1000;           // transmission interval (milliseconds)

void setup() {
  Serial3.begin(115200);
  //bool ret = CANInit(CAN_500KBPS, 0);  // CAN_RX mapped to PA11, CAN_TX mapped to PA12
  bool ret = CANInit(CAN_125KBPS, 2);  // CAN_RX mapped to PB8, CAN_TX mapped to PB9
  //bool ret = CANInit(CAN_1000KBPS, 0);  // CAN_RX mapped to PA11, CAN_TX mapped to PA12
  //bool ret = CANInit(CAN_1000KBPS, 2);  // CAN_RX mapped to PB8, CAN_TX mapped to PB9
  //bool ret = CANInit(CAN_1000KBPS, 3);  // CAN_RX mapped to PD0, CAN_TX mapped to PD1
  if (!ret) while(true);
}

void loop() {
  CAN_msg_t CAN_TX_msg;
  CAN_msg_t CAN_RX_msg;

  CAN_TX_msg.data[0] = 0x00;
  CAN_TX_msg.data[1] = 0x01;
  CAN_TX_msg.data[2] = 0x02;
  CAN_TX_msg.data[3] = 0x03;
  CAN_TX_msg.data[4] = 0x04;
  CAN_TX_msg.data[5] = 0x05;
  CAN_TX_msg.data[6] = 0x06;
  CAN_TX_msg.data[7] = 0x07;
  CAN_TX_msg.len = frameLength;

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    if ( ( counter % 2) == 0) {
      CAN_TX_msg.type = DATA_FRAME;
      if (CAN_TX_msg.len == 0) CAN_TX_msg.type = REMOTE_FRAME;
      CAN_TX_msg.format = STANDARD_FORMAT;
      CAN_TX_msg.id = 0x000;
    } else {
      CAN_TX_msg.type = DATA_FRAME;
      if (CAN_TX_msg.len == 0) CAN_TX_msg.type = REMOTE_FRAME;
      CAN_TX_msg.format = STANDARD_FORMAT;
      CAN_TX_msg.id = 0x400;
    }
    CANSend(&CAN_TX_msg);
    frameLength++;
    if (frameLength == 9) frameLength = 0;
    counter++;
  }
 
  if(CANMsgAvail()) {
    CANReceive(&CAN_RX_msg);

    if (CAN_RX_msg.format == EXTENDED_FORMAT) {
      Serial3.print("Extended ID: 0x");
      if (CAN_RX_msg.id < 0x10000000) Serial3.print("0");
      if (CAN_RX_msg.id < 0x1000000) Serial3.print("00");
      if (CAN_RX_msg.id < 0x100000) Serial3.print("000");
      if (CAN_RX_msg.id < 0x10000) Serial3.print("0000");
      Serial3.print(CAN_RX_msg.id, HEX);
    } else {
      Serial3.print("Standard ID: 0x");
      if (CAN_RX_msg.id < 0x100) Serial3.print("0");
      if (CAN_RX_msg.id < 0x10) Serial3.print("00");
      Serial3.print(CAN_RX_msg.id, HEX);
      Serial3.print("     ");
    }

    Serial3.print(" DLC: ");
    Serial3.print(CAN_RX_msg.len);
    if (CAN_RX_msg.type == DATA_FRAME) {
      Serial3.print(" Data: ");
      for(int i=0; i<CAN_RX_msg.len; i++) {
        Serial3.print("0x");
        Serial3.print(CAN_RX_msg.data[i], HEX);
        if (i != (CAN_RX_msg.len-1))  Serial3.print(" ");
      }
      Serial3.println();
    } else {
      Serial3.println(" Data: REMOTE REQUEST FRAME");
    }
  }
   
 

}
« Last Edit: May 14, 2021, 02:31:56 am by poot36 »
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
I have made some progress with this code, the screen now flashes (I think it is still resetting).

Code: [Select]
uint8_t counter = 0;
uint8_t frameLength = 0;
uint8_t frameLength1 = 0;
unsigned long previousMillis = 0;     // stores last time output was updated
const long interval = 1000;           // transmission interval (milliseconds)

void setup() {
  Serial3.begin(115200);
  //bool ret = CANInit(CAN_500KBPS, 0);  // CAN_RX mapped to PA11, CAN_TX mapped to PA12
  bool ret = CANInit(CAN_125KBPS, 2);  // CAN_RX mapped to PB8, CAN_TX mapped to PB9
  //bool ret = CANInit(CAN_1000KBPS, 0);  // CAN_RX mapped to PA11, CAN_TX mapped to PA12
  //bool ret = CANInit(CAN_1000KBPS, 2);  // CAN_RX mapped to PB8, CAN_TX mapped to PB9
  //bool ret = CANInit(CAN_1000KBPS, 3);  // CAN_RX mapped to PD0, CAN_TX mapped to PD1
  if (!ret) while(true);
}

void loop() {
  CAN_msg_t CAN_TX_msg;
  CAN_msg_t CAN_TX_msg1;
  CAN_msg_t CAN_RX_msg;

  CAN_TX_msg.data[0] = 0xFD;
  CAN_TX_msg.data[1] = 0x16;
  CAN_TX_msg.data[2] = 0x3F;
  CAN_TX_msg.data[3] = 0xFF;
  CAN_TX_msg.data[4] = 0xFF;
  CAN_TX_msg.data[5] = 0xFF;
  CAN_TX_msg.data[6] = 0xFF;
  CAN_TX_msg.data[7] = 0xFF;
  CAN_TX_msg.len = frameLength;

  CAN_TX_msg1.data[0] = 0x63;
  CAN_TX_msg1.data[1] = 0x00;
  CAN_TX_msg1.data[2] = 0x00;
  CAN_TX_msg1.data[3] = 0x00;
  CAN_TX_msg1.data[4] = 0x00;
  CAN_TX_msg1.data[5] = 0x00;
  CAN_TX_msg1.len = frameLength1;

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    if ( ( counter % 2) == 0) {
      CAN_TX_msg1.type = DATA_FRAME;
      if (CAN_TX_msg1.len == 0) CAN_TX_msg1.type = REMOTE_FRAME;
      CAN_TX_msg1.format = STANDARD_FORMAT;
      CAN_TX_msg1.id = 0x000;
    } else {
      CAN_TX_msg.type = DATA_FRAME;
      if (CAN_TX_msg.len == 0) CAN_TX_msg.type = REMOTE_FRAME;
      CAN_TX_msg.format = STANDARD_FORMAT;
      CAN_TX_msg.id = 0x415;
    }
    CANSend(&CAN_TX_msg);
    frameLength++;
    if (frameLength == 9) frameLength = 0;

    CANSend(&CAN_TX_msg1);
    frameLength1++;
    if (frameLength1 == 7) frameLength1 = 0;
    counter++;
  }

Not sure if I can do this
CAN_msg_t CAN_TX_msg;
 CAN_msg_t CAN_TX_msg1;
in the code though.
 

Offline dc101

  • Regular Contributor
  • *
  • Posts: 220
  • Country: us
I saw the forum post you linked, I just wasn't able to tell from the messages if it was related to your radio or not.

Not sure what you mean by 6 bit and 8 bit codes? Are you talking about the can bus ID or the data field or something else? https://copperhilltech.com/blog/controller-area-network-can-bus-message-frame-architecture/

There are base frames and extended frames in can bus, base frames have an 11 bit id, and extended frames have a 29 bit id. https://www.kvaser.com/about-can/the-can-protocol/can-messages-13/

The biggest issue I had when first writing code to send can bus messages with my Keil dev board was getting the format of the message correct in the various registers. It's been a few years, but I think it was an endian issue that was messing me up.
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
I think I do not understand the can protocol very well, in the screenshot it shows by the ID 000 a 6 and only 6 sections of 2 bit hex data.  That is what I am trying to send but I am not sure if the code that I have even supports that.  I had the opportunity to see what the radio was doing in the coworkers truck and it was doing the oddest things.  Sometimes it would not power up at all, some times it would just show the power up splash screen, some times it would let you in and select various options before completely locking up.  I even had it stay turned on in the absence of can data so that may explain them having a flat battery some of the time.  I also have had it where the radio would work it then froze up but the radio kept playing until you turned off the ignition but the screen was still showing the radio page with the ignition off but it looked like it was not getting updated because there was odd colored lines around most of the square boxes and some of the button text was gone.  If you turned the ignition back on the radio would keep playing.  Very odd.  I think the main CPU is having issues as the display and freezing issues most likely point to that.  I really need to get this running on the bench to be able to probe voltages and clock signals as well as reset signals in the radio.
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
Ok, so I have made some progress, the resetting with screen flashing was caused by a missing Can bus data line (clip was not making good connection).  I have fixed that and now when I send the power up code ID 0x000 data 0x63 0x00 0x00 0x00 0x00 0x00 I now get the illumination for the buttons to glow and the eject button now works.  I am still struggling with sending an additional code after this one with the code I have now.
Code: [Select]
#define DEBUG 0

//                      RX    TX
HardwareSerial Serial3(PA10, PA9);

/* Symbolic names for bit rate of CAN message                                */
typedef enum {CAN_50KBPS, CAN_100KBPS, CAN_125KBPS, CAN_250KBPS, CAN_500KBPS, CAN_1000KBPS} BITRATE;

/* Symbolic names for formats of CAN message                                 */
typedef enum {STANDARD_FORMAT = 0, EXTENDED_FORMAT} CAN_FORMAT;

/* Symbolic names for type of CAN message                                    */
typedef enum {DATA_FRAME = 0, REMOTE_FRAME}         CAN_FRAME;


typedef struct
{
  uint32_t id;        /* 29 bit identifier                               */
  uint8_t  data[8];   /* Data field                                      */
  uint8_t  len;       /* Length of data field in bytes                   */
  uint8_t  ch;        /* Object channel(Not use)                         */
  uint8_t  format;    /* 0 - STANDARD, 1- EXTENDED IDENTIFIER            */
  uint8_t  type;      /* 0 - DATA FRAME, 1 - REMOTE FRAME                */
} CAN_msg_t;

typedef const struct
{
  uint8_t TS2;
  uint8_t TS1;
  uint8_t BRP;
} CAN_bit_timing_config_t;

CAN_bit_timing_config_t can_configs[6] = {{2, 13, 45}, {2, 15, 20}, {2, 13, 18}, {2, 13, 9}, {2, 15, 4}, {2, 15, 2}};

/**
 * Print registers.
*/
void printRegister(char * buf, uint32_t reg) {
  if (DEBUG == 0) return;
  Serial3.print(buf);
  Serial3.print(reg, HEX);
  Serial3.println();
}

/**
 * Initializes the CAN filter registers.
 *
 * @preconditions   - This register can be written only when the filter initialization mode is set (FINIT=1) in the CAN_FMR register.
 * @params: index   - Specified filter index. index 27:14 are available in connectivity line devices only.
 * @params: scale   - Select filter scale.
 *                    0: Dual 16-bit scale configuration
 *                    1: Single 32-bit scale configuration
 * @params: mode    - Select filter mode.
 *                    0: Two 32-bit registers of filter bank x are in Identifier Mask mode
 *                    1: Two 32-bit registers of filter bank x are in Identifier List mode
 * @params: fifo    - Select filter assigned.
 *                    0: Filter assigned to FIFO 0
 *                    1: Filter assigned to FIFO 1
 * @params: bank1   - Filter bank register 1
 * @params: bank2   - Filter bank register 2
 *
 */
void CANSetFilter(uint8_t index, uint8_t scale, uint8_t mode, uint8_t fifo, uint32_t bank1, uint32_t bank2) {
  if (index > 27) return;

  CAN1->FA1R &= ~(0x1UL<<index);               // Deactivate filter

  if (scale == 0) {
    CAN1->FS1R &= ~(0x1UL<<index);             // Set filter to Dual 16-bit scale configuration
  } else {
    CAN1->FS1R |= (0x1UL<<index);              // Set filter to single 32 bit configuration
  }
    if (mode == 0) {
    CAN1->FM1R &= ~(0x1UL<<index);             // Set filter to Mask mode
  } else {
    CAN1->FM1R |= (0x1UL<<index);              // Set filter to List mode
  }

  if (fifo == 0) {
    CAN1->FFA1R &= ~(0x1UL<<index);            // Set filter assigned to FIFO 0
  } else {
    CAN1->FFA1R |= (0x1UL<<index);             // Set filter assigned to FIFO 1
  }

  CAN1->sFilterRegister[index].FR1 = bank1;    // Set filter bank registers1
  CAN1->sFilterRegister[index].FR2 = bank2;    // Set filter bank registers2

  CAN1->FA1R |= (0x1UL<<index);                // Activate filter

}

/**
 * Initializes the CAN controller with specified bit rate.
 *
 * @params: bitrate - Specified bitrate. If this value is not one of the defined constants, bit rate will be defaulted to 125KBS
 * @params: remap   - Select CAN port.
 *                    =0:CAN_RX mapped to PA11, CAN_TX mapped to PA12
 *                    =1:Not used
 *                    =2:CAN_RX mapped to PB8, CAN_TX mapped to PB9 (not available on 36-pin package)
 *                    =3:CAN_RX mapped to PD0, CAN_TX mapped to PD1 (available on 100-pin and 144-pin package)
 *
 */
bool CANInit(BITRATE bitrate, int remap)
{
  // Reference manual
  // [url]https://www.st.com/content/ccc/resource/technical/document/reference_manual/59/b9/ba/7f/11/af/43/d5/CD00171190.pdf/files/CD00171190.pdf/jcr:content/translations/en.CD00171190.pdf[/url]

  RCC->APB1ENR |= 0x2000000UL;       // Enable CAN clock
  RCC->APB2ENR |= 0x1UL;             // Enable AFIO clock
  AFIO->MAPR   &= 0xFFFF9FFF;        // reset CAN remap
                                     // CAN_RX mapped to PA11, CAN_TX mapped to PA12

  if (remap == 0) {
    RCC->APB2ENR |= 0x4UL;           // Enable GPIOA clock
    GPIOA->CRH   &= ~(0xFF000UL);    // Configure PA12(0b0000) and PA11(0b0000)
                                     // 0b0000
                                     //   MODE=00(Input mode)
                                     //   CNF=00(Analog mode)

    GPIOA->CRH   |= 0xB8FFFUL;       // Configure PA12(0b1011) and PA11(0b1000)
                                     // 0b1011
                                     //   MODE=11(Output mode, max speed 50 MHz)
                                     //   CNF=10(Alternate function output Push-pull
                                     // 0b1000
                                     //   MODE=00(Input mode)
                                     //   CNF=10(Input with pull-up / pull-down)
                                     
    GPIOA->ODR |= 0x1UL << 12;       // PA12 Upll-up
   
  }
                               
  if (remap == 2) {
    AFIO->MAPR   |= 0x00004000;      // set CAN remap
                                     // CAN_RX mapped to PB8, CAN_TX mapped to PB9 (not available on 36-pin package)

    RCC->APB2ENR |= 0x8UL;           // Enable GPIOB clock
    GPIOB->CRH   &= ~(0xFFUL);       // Configure PB9(0b0000) and PB8(0b0000)
                                     // 0b0000
                                     //   MODE=00(Input mode)
                                     //   CNF=00(Analog mode)

    GPIOB->CRH   |= 0xB8UL;          // Configure PB9(0b1011) and PB8(0b1000)
                                     // 0b1011
                                     //   MODE=11(Output mode, max speed 50 MHz)
                                     //   CNF=10(Alternate function output Push-pull
                                     // 0b1000
                                     //   MODE=00(Input mode)
                                     //   CNF=10(Input with pull-up / pull-down)
                                     
    GPIOB->ODR |= 0x1UL << 8;        // PB8 Upll-up
  }
 
  if (remap == 3) {
    AFIO->MAPR   |= 0x00005000;      // set CAN remap
                                     // CAN_RX mapped to PD0, CAN_TX mapped to PD1 (available on 100-pin and 144-pin package)

    RCC->APB2ENR |= 0x20UL;          // Enable GPIOD clock
    GPIOD->CRL   &= ~(0xFFUL);       // Configure PD1(0b0000) and PD0(0b0000)
                                     // 0b0000
                                     //   MODE=00(Input mode)
                                     //   CNF=00(Analog mode)

    GPIOD->CRH   |= 0xB8UL;          // Configure PD1(0b1011) and PD0(0b1000)
                                     // 0b1000
                                     //   MODE=00(Input mode)
                                     //   CNF=10(Input with pull-up / pull-down)
                                     // 0b1011
                                     //   MODE=11(Output mode, max speed 50 MHz)
                                     //   CNF=10(Alternate function output Push-pull
                                     
    GPIOD->ODR |= 0x1UL << 0;        // PD0 Upll-up
  }

  CAN1->MCR |= 0x1UL;                   // Require CAN1 to Initialization mode
  while (!(CAN1->MSR & 0x1UL));         // Wait for Initialization mode

  //CAN1->MCR = 0x51UL;                 // Hardware initialization(No automatic retransmission)
  CAN1->MCR = 0x41UL;                   // Hardware initialization(With automatic retransmission)
   
  // Set bit rates
  CAN1->BTR &= ~(((0x03) << 24) | ((0x07) << 20) | ((0x0F) << 16) | (0x1FF));
  CAN1->BTR |=  (((can_configs[bitrate].TS2-1) & 0x07) << 20) | (((can_configs[bitrate].TS1-1) & 0x0F) << 16) | ((can_configs[bitrate].BRP-1) & 0x1FF);

  // Configure Filters to default values
  CAN1->FMR  |=   0x1UL;                // Set to filter initialization mode
  CAN1->FMR  &= 0xFFFFC0FF;             // Clear CAN2 start bank

  // bxCAN has 28 filters.
  // These filters are used for both CAN1 and CAN2.
  // STM32F103 has only CAN1, so all 28 are used for CAN1
  CAN1->FMR  |= 0x1C << 8;              // Assign all filters to CAN1

  // Set fileter 0
  // Single 32-bit scale configuration
  // Two 32-bit registers of filter bank x are in Identifier Mask mode
  // Filter assigned to FIFO 0
  // Filter bank register to all 0
  CANSetFilter(0, 1, 0, 0, 0x0UL, 0x0UL);
 
  CAN1->FMR   &= ~(0x1UL);              // Deactivate initialization mode

  uint16_t TimeoutMilliseconds = 1000;
  bool can1 = false;
  CAN1->MCR   &= ~(0x1UL);              // Require CAN1 to normal mode

  // Wait for normal mode
  // If the connection is not correct, it will not return to normal mode.
  for (uint16_t wait_ack = 0; wait_ack < TimeoutMilliseconds; wait_ack++) {
    if ((CAN1->MSR & 0x1UL) == 0) {
      can1 = true;
      break;
    }
    delayMicroseconds(1000);
  }
  //Serial3.print("can1=");
  //Serial3.println(can1);
  if (can1) {
    Serial3.println("CAN1 initialize ok");
  } else {
    Serial3.println("CAN1 initialize fail!!");
    return false;
  }
  return true;
}


#define STM32_CAN_TIR_TXRQ              (1U << 0U)  // Bit 0: Transmit Mailbox Request
#define STM32_CAN_RIR_RTR               (1U << 1U)  // Bit 1: Remote Transmission Request
#define STM32_CAN_RIR_IDE               (1U << 2U)  // Bit 2: Identifier Extension
#define STM32_CAN_TIR_RTR               (1U << 1U)  // Bit 1: Remote Transmission Request
#define STM32_CAN_TIR_IDE               (1U << 2U)  // Bit 2: Identifier Extension

#define CAN_EXT_ID_MASK                 0x1FFFFFFFU
#define CAN_STD_ID_MASK                 0x000007FFU
 
/**
 * Decodes CAN messages from the data registers and populates a
 * CAN message struct with the data fields.
 *
 * @preconditions A valid CAN message is received
 * @params CAN_rx_msg - CAN message structure for reception
 *
 */
void CANReceive(CAN_msg_t* CAN_rx_msg)
{
  uint32_t id = CAN1->sFIFOMailBox[0].RIR;
  if ((id & STM32_CAN_RIR_IDE) == 0) { // Standard frame format
      CAN_rx_msg->format = STANDARD_FORMAT;;
      CAN_rx_msg->id = (CAN_STD_ID_MASK & (id >> 21U));
  }
  else {                               // Extended frame format
      CAN_rx_msg->format = EXTENDED_FORMAT;;
      CAN_rx_msg->id = (CAN_EXT_ID_MASK & (id >> 3U));
  }

  if ((id & STM32_CAN_RIR_RTR) == 0) { // Data frame
      CAN_rx_msg->type = DATA_FRAME;
  }
  else {                               // Remote frame
      CAN_rx_msg->type = REMOTE_FRAME;
  }

 
  CAN_rx_msg->len = (CAN1->sFIFOMailBox[0].RDTR) & 0xFUL;
 
  CAN_rx_msg->data[0] = 0xFFUL &  CAN1->sFIFOMailBox[0].RDLR;
  CAN_rx_msg->data[1] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDLR >> 8);
  CAN_rx_msg->data[2] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDLR >> 16);
  CAN_rx_msg->data[3] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDLR >> 24);
  CAN_rx_msg->data[4] = 0xFFUL &  CAN1->sFIFOMailBox[0].RDHR;
  CAN_rx_msg->data[5] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDHR >> 8);
  CAN_rx_msg->data[6] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDHR >> 16);
  CAN_rx_msg->data[7] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDHR >> 24);

  // Release FIFO 0 output mailbox.
  // Make the next incoming message available.
  CAN1->RF0R |= 0x20UL;
}
 
/**
 * Encodes CAN messages using the CAN message struct and populates the
 * data registers with the sent.
 *
 * @params CAN_tx_msg - CAN message structure for transmission
 *
 */
void CANSend(CAN_msg_t* CAN_tx_msg)
{
  volatile int count = 0;

  uint32_t out = 0;
  if (CAN_tx_msg->format == EXTENDED_FORMAT) { // Extended frame format
      out = ((CAN_tx_msg->id & CAN_EXT_ID_MASK) << 3U) | STM32_CAN_TIR_IDE;
  }
  else {                                       // Standard frame format
      out = ((CAN_tx_msg->id & CAN_STD_ID_MASK) << 21U);
  }

  // Remote frame
  if (CAN_tx_msg->type == REMOTE_FRAME) {
      out |= STM32_CAN_TIR_RTR;
  }

  CAN1->sTxMailBox[0].TDTR &= ~(0xF);
  CAN1->sTxMailBox[0].TDTR |= CAN_tx_msg->len & 0xFUL;
 
  CAN1->sTxMailBox[0].TDLR  = (((uint32_t) CAN_tx_msg->data[3] << 24) |
                               ((uint32_t) CAN_tx_msg->data[2] << 16) |
                               ((uint32_t) CAN_tx_msg->data[1] <<  8) |
                               ((uint32_t) CAN_tx_msg->data[0]      ));
  CAN1->sTxMailBox[0].TDHR  = (((uint32_t) CAN_tx_msg->data[7] << 24) |
                               ((uint32_t) CAN_tx_msg->data[6] << 16) |
                               ((uint32_t) CAN_tx_msg->data[5] <<  8) |
                               ((uint32_t) CAN_tx_msg->data[4]      ));

  // Send Go
  CAN1->sTxMailBox[0].TIR = out | STM32_CAN_TIR_TXRQ;

  // Wait until the mailbox is empty
  while(CAN1->sTxMailBox[0].TIR & 0x1UL && count++ < 1000000);

  // The mailbox don't becomes empty while loop
  if (CAN1->sTxMailBox[0].TIR & 0x1UL) {
    Serial3.println("Send Fail");
    Serial3.println(CAN1->ESR);
    Serial3.println(CAN1->MSR);
    Serial3.println(CAN1->TSR);
  }
}

 /**
 * Returns whether there are CAN messages available.
 *
 * @returns If pending CAN messages are in the CAN controller
 *
 */
uint8_t CANMsgAvail(void)
{
  // Check for pending FIFO 0 messages
  return CAN1->RF0R & 0x3UL;
}


uint8_t counter = 0;
uint8_t counter1 = 0;
uint8_t frameLength = 0;
uint8_t frameLength1 = 0;
unsigned long previousMillis = 0;     // stores last time output was updated
unsigned long previousMillis1 = 0;
const long interval = 200;           // transmission interval (milliseconds)
const long interval1 = 200;

void setup() {
  Serial3.begin(115200);
  //bool ret = CANInit(CAN_500KBPS, 0);  // CAN_RX mapped to PA11, CAN_TX mapped to PA12
  bool ret = CANInit(CAN_125KBPS, 2);  // CAN_RX mapped to PB8, CAN_TX mapped to PB9
  //bool ret = CANInit(CAN_1000KBPS, 0);  // CAN_RX mapped to PA11, CAN_TX mapped to PA12
  //bool ret = CANInit(CAN_1000KBPS, 2);  // CAN_RX mapped to PB8, CAN_TX mapped to PB9
  //bool ret = CANInit(CAN_1000KBPS, 3);  // CAN_RX mapped to PD0, CAN_TX mapped to PD1
  if (!ret) while(true);
}

void loop() {
 
  CAN_msg_t CAN_TX_msg1;
  CAN_msg_t CAN_RX_msg;
 
  CAN_TX_msg1.data[0] = 0x63;
  CAN_TX_msg1.data[1] = 0x00;
  CAN_TX_msg1.data[2] = 0x00;
  CAN_TX_msg1.data[3] = 0x00;
  CAN_TX_msg1.data[4] = 0x00;
  CAN_TX_msg1.data[5] = 0x00;
  CAN_TX_msg1.len = frameLength1;

  unsigned long currentMillis1 = millis();
  if (currentMillis1 - previousMillis1 >= interval1) {
    previousMillis1 = currentMillis1;
    if ( ( counter1 % 2) == 0) {
      CAN_TX_msg1.type = DATA_FRAME;
      if (CAN_TX_msg1.len == 0) CAN_TX_msg1.type = REMOTE_FRAME;
      CAN_TX_msg1.format = STANDARD_FORMAT;
      CAN_TX_msg1.id = 0x000;
    }
    CANSend(&CAN_TX_msg1);
    frameLength1++;
    if (frameLength1 == 7) frameLength1 = 0;
    counter1++;
  }

  CAN_msg_t CAN_TX_msg;

  CAN_TX_msg.data[0] = 0xFD;
  CAN_TX_msg.data[1] = 0x16;
  CAN_TX_msg.data[2] = 0x3F;
  CAN_TX_msg.data[3] = 0xFF;
  CAN_TX_msg.data[4] = 0xFF;
  CAN_TX_msg.data[5] = 0xFF;
  CAN_TX_msg.data[6] = 0xFF;
  CAN_TX_msg.data[7] = 0xFF;
  CAN_TX_msg.len = frameLength;

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    if ( ( counter % 2) == 0) {
      CAN_TX_msg.type = DATA_FRAME;
      if (CAN_TX_msg.len == 0) CAN_TX_msg.type = REMOTE_FRAME;
      CAN_TX_msg.format = STANDARD_FORMAT;
      CAN_TX_msg.id = 0x415;
    }
    //CANSend(&CAN_TX_msg);
    frameLength++;
    if (frameLength == 9) frameLength = 0;
    counter++;
  }
 
  if(CANMsgAvail()) {
    CANReceive(&CAN_RX_msg);

    if (CAN_RX_msg.format == EXTENDED_FORMAT) {
      Serial3.print("Extended ID: 0x");
      if (CAN_RX_msg.id < 0x10000000) Serial3.print("0");
      if (CAN_RX_msg.id < 0x1000000) Serial3.print("00");
      if (CAN_RX_msg.id < 0x100000) Serial3.print("000");
      if (CAN_RX_msg.id < 0x10000) Serial3.print("0000");
      Serial3.print(CAN_RX_msg.id, HEX);
    } else {
      Serial3.print("Standard ID: 0x");
      if (CAN_RX_msg.id < 0x100) Serial3.print("0");
      if (CAN_RX_msg.id < 0x10) Serial3.print("00");
      Serial3.print(CAN_RX_msg.id, HEX);
      Serial3.print("     ");
    }

    Serial3.print(" DLC: ");
    Serial3.print(CAN_RX_msg.len);
    if (CAN_RX_msg.type == DATA_FRAME) {
      Serial3.print(" Data: ");
      for(int i=0; i<CAN_RX_msg.len; i++) {
        Serial3.print("0x");
        Serial3.print(CAN_RX_msg.data[i], HEX);
        if (i != (CAN_RX_msg.len-1))  Serial3.print(" ");
      }
      Serial3.println();
    } else {
      Serial3.println(" Data: REMOTE REQUEST FRAME");
    }
  }
   
 

}


I can change between sending the power on code and the radio ID code ID 0x415 data 0xFD 0x16 0x3F 0xFF 0xFF 0xFF 0xFF 0xFF by commenting out either the CANSend(&CAN_TX_msg1); or the CANSend(&CAN_TX_msg); lines.  I have found that if I first run it with the power on code and then reprogram it with the radio ID code I have been able to get the boot logo to appear when I press the eject button but it then goes away.  My issue is getting it to send both codes in sequence with the correct timing between them.  If I just send the power on code I can get the eject button to work and the buttons to illuminate but no display and if I just send the radio ID code nothing happens but the current consumption goes to around 2 Amps so it is sort of powered up (around this much current is also drawn with the power up code as well).  Got any ideas why I can not get this code to send 2 Can bus codes in sequence?  I really suck at programming and am mostly a hardware person.
 

Offline YetAnotherTechie

  • Regular Contributor
  • *
  • Posts: 222
  • Country: pt
How long have you spent on this?? As i understood, you're not doing it for fun, part of managing a repair is knowing when to say "no". Is your coworker going to pay you for 30hours of work? isn't he upset about not having the radio?
 

Offline SilverSolder

  • Super Contributor
  • ***
  • Posts: 6126
  • Country: 00

Got to love modern technology, even turning on a radio has been turned into a crazy project lol

The average cost of a new car in the US is now north of $38K, and this is the kind of stuff you get for your money!
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
@YetAnotherTechie I don't really know how much time I have spent on this.  It is a learning experience and well it is not exactly for fun I am having fun learning.  I am not in it for the money and the radio has never worked correctly since they got the truck used so they are not missing it.

@SilverSolder As far as I can tell they have been doing this since around 2005 or so where the Can bus is used to power up the radio.  Not easy to trouble shoot is correct.  If I can get this figured out then anyone with a STM32 blue pill and a compatible Can bus transceiver chip can make this work without having to spend $30 or more on custom made devices to do the same job as at most $10 worth of parts.
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
Been doing some more testing and have found that the RXD pin (this one sends data into the radio) on the TJA1041A Can bus transceiver chip (datasheet: https://www.nxp.com/docs/en/data-sheet/TJA1041A.pdf ) in the radio matches the data sent by the RX pin on the STM32 micro for the most part how ever the TXD pin on radio and the TX pin on the STM32 do not match very frequently.  Not sure if this is an issue or not.  I have also had the radio power up fully for a few seconds when just sending the Power on command without the Radio ID command although this could be due to my crappy alligator chips briefly loosing connection and some other corrupted data being sent to the unit.  I also probed the INH pin on the TJA1041A chip and found that it occasionally goes low disabling all the power supplies in the radio although this could be because I am not sending the Radio ID command and it is rebooting because of that.  Also have noticed that most of the big processing chips in the unit run around 70C or more, is that normal?  Do you think I could get the code to send the 2 codes I need by placing them in individual functions and calling them from the loop section in the code?  Kind of like this code I found online? https://github.com/alex3dbros/LGH1ScooterPackPower/blob/master/Jump_Start.ino
« Last Edit: May 24, 2021, 11:51:23 pm by poot36 »
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
Here is the latest code that will turn on the illumination but nothing else.  It should be sending the Radio ID code as well but I am unable to confirm that.  I have found that the radio is way more likely to power up with the illumination if I power it up after I power on the STM32 board.

Code: [Select]
#define DEBUG 0

//                      RX    TX
HardwareSerial Serial3(PA10, PA9);

/* Symbolic names for bit rate of CAN message                                */
typedef enum {CAN_50KBPS, CAN_100KBPS, CAN_125KBPS, CAN_250KBPS, CAN_500KBPS, CAN_1000KBPS} BITRATE;

/* Symbolic names for formats of CAN message                                 */
typedef enum {STANDARD_FORMAT = 0, EXTENDED_FORMAT} CAN_FORMAT;

/* Symbolic names for type of CAN message                                    */
typedef enum {DATA_FRAME = 0, REMOTE_FRAME}         CAN_FRAME;


typedef struct
{
  uint32_t id;        /* 29 bit identifier                               */
  uint8_t  data[8];   /* Data field                                      */
  uint8_t  len;       /* Length of data field in bytes                   */
  uint8_t  ch;        /* Object channel(Not use)                         */
  uint8_t  format;    /* 0 - STANDARD, 1- EXTENDED IDENTIFIER            */
  uint8_t  type;      /* 0 - DATA FRAME, 1 - REMOTE FRAME                */
} CAN_msg_t;

typedef const struct
{
  uint8_t TS2;
  uint8_t TS1;
  uint8_t BRP;
} CAN_bit_timing_config_t;

CAN_bit_timing_config_t can_configs[6] = {{2, 13, 45}, {2, 15, 20}, {2, 13, 18}, {2, 13, 9}, {2, 15, 4}, {2, 15, 2}};

/**
 * Print registers.
*/
void printRegister(char * buf, uint32_t reg) {
  if (DEBUG == 0) return;
  Serial3.print(buf);
  Serial3.print(reg, HEX);
  Serial3.println();
}

/**
 * Initializes the CAN filter registers.
 *
 * @preconditions   - This register can be written only when the filter initialization mode is set (FINIT=1) in the CAN_FMR register.
 * @params: index   - Specified filter index. index 27:14 are available in connectivity line devices only.
 * @params: scale   - Select filter scale.
 *                    0: Dual 16-bit scale configuration
 *                    1: Single 32-bit scale configuration
 * @params: mode    - Select filter mode.
 *                    0: Two 32-bit registers of filter bank x are in Identifier Mask mode
 *                    1: Two 32-bit registers of filter bank x are in Identifier List mode
 * @params: fifo    - Select filter assigned.
 *                    0: Filter assigned to FIFO 0
 *                    1: Filter assigned to FIFO 1
 * @params: bank1   - Filter bank register 1
 * @params: bank2   - Filter bank register 2
 *
 */
void CANSetFilter(uint8_t index, uint8_t scale, uint8_t mode, uint8_t fifo, uint32_t bank1, uint32_t bank2) {
  if (index > 27) return;

  CAN1->FA1R &= ~(0x1UL<<index);               // Deactivate filter

  if (scale == 0) {
    CAN1->FS1R &= ~(0x1UL<<index);             // Set filter to Dual 16-bit scale configuration
  } else {
    CAN1->FS1R |= (0x1UL<<index);              // Set filter to single 32 bit configuration
  }
    if (mode == 0) {
    CAN1->FM1R &= ~(0x1UL<<index);             // Set filter to Mask mode
  } else {
    CAN1->FM1R |= (0x1UL<<index);              // Set filter to List mode
  }

  if (fifo == 0) {
    CAN1->FFA1R &= ~(0x1UL<<index);            // Set filter assigned to FIFO 0
  } else {
    CAN1->FFA1R |= (0x1UL<<index);             // Set filter assigned to FIFO 1
  }

  CAN1->sFilterRegister[index].FR1 = bank1;    // Set filter bank registers1
  CAN1->sFilterRegister[index].FR2 = bank2;    // Set filter bank registers2

  CAN1->FA1R |= (0x1UL<<index);                // Activate filter

}

/**
 * Initializes the CAN controller with specified bit rate.
 *
 * @params: bitrate - Specified bitrate. If this value is not one of the defined constants, bit rate will be defaulted to 125KBS
 * @params: remap   - Select CAN port.
 *                    =0:CAN_RX mapped to PA11, CAN_TX mapped to PA12
 *                    =1:Not used
 *                    =2:CAN_RX mapped to PB8, CAN_TX mapped to PB9 (not available on 36-pin package)
 *                    =3:CAN_RX mapped to PD0, CAN_TX mapped to PD1 (available on 100-pin and 144-pin package)
 *
 */
bool CANInit(BITRATE bitrate, int remap)
{
  // Reference manual
  // [url]https://www.st.com/content/ccc/resource/technical/document/reference_manual/59/b9/ba/7f/11/af/43/d5/CD00171190.pdf/files/CD00171190.pdf/jcr:content/translations/en.CD00171190.pdf[/url]

  RCC->APB1ENR |= 0x2000000UL;       // Enable CAN clock
  RCC->APB2ENR |= 0x1UL;             // Enable AFIO clock
  AFIO->MAPR   &= 0xFFFF9FFF;        // reset CAN remap
                                     // CAN_RX mapped to PA11, CAN_TX mapped to PA12

  if (remap == 0) {
    RCC->APB2ENR |= 0x4UL;           // Enable GPIOA clock
    GPIOA->CRH   &= ~(0xFF000UL);    // Configure PA12(0b0000) and PA11(0b0000)
                                     // 0b0000
                                     //   MODE=00(Input mode)
                                     //   CNF=00(Analog mode)

    GPIOA->CRH   |= 0xB8FFFUL;       // Configure PA12(0b1011) and PA11(0b1000)
                                     // 0b1011
                                     //   MODE=11(Output mode, max speed 50 MHz)
                                     //   CNF=10(Alternate function output Push-pull
                                     // 0b1000
                                     //   MODE=00(Input mode)
                                     //   CNF=10(Input with pull-up / pull-down)
                                     
    GPIOA->ODR |= 0x1UL << 12;       // PA12 Upll-up
   
  }
                               
  if (remap == 2) {
    AFIO->MAPR   |= 0x00004000;      // set CAN remap
                                     // CAN_RX mapped to PB8, CAN_TX mapped to PB9 (not available on 36-pin package)

    RCC->APB2ENR |= 0x8UL;           // Enable GPIOB clock
    GPIOB->CRH   &= ~(0xFFUL);       // Configure PB9(0b0000) and PB8(0b0000)
                                     // 0b0000
                                     //   MODE=00(Input mode)
                                     //   CNF=00(Analog mode)

    GPIOB->CRH   |= 0xB8UL;          // Configure PB9(0b1011) and PB8(0b1000)
                                     // 0b1011
                                     //   MODE=11(Output mode, max speed 50 MHz)
                                     //   CNF=10(Alternate function output Push-pull
                                     // 0b1000
                                     //   MODE=00(Input mode)
                                     //   CNF=10(Input with pull-up / pull-down)
                                     
    GPIOB->ODR |= 0x1UL << 8;        // PB8 Upll-up
  }
 
  if (remap == 3) {
    AFIO->MAPR   |= 0x00005000;      // set CAN remap
                                     // CAN_RX mapped to PD0, CAN_TX mapped to PD1 (available on 100-pin and 144-pin package)

    RCC->APB2ENR |= 0x20UL;          // Enable GPIOD clock
    GPIOD->CRL   &= ~(0xFFUL);       // Configure PD1(0b0000) and PD0(0b0000)
                                     // 0b0000
                                     //   MODE=00(Input mode)
                                     //   CNF=00(Analog mode)

    GPIOD->CRH   |= 0xB8UL;          // Configure PD1(0b1011) and PD0(0b1000)
                                     // 0b1000
                                     //   MODE=00(Input mode)
                                     //   CNF=10(Input with pull-up / pull-down)
                                     // 0b1011
                                     //   MODE=11(Output mode, max speed 50 MHz)
                                     //   CNF=10(Alternate function output Push-pull
                                     
    GPIOD->ODR |= 0x1UL << 0;        // PD0 Upll-up
  }

  CAN1->MCR |= 0x1UL;                   // Require CAN1 to Initialization mode
  while (!(CAN1->MSR & 0x1UL));         // Wait for Initialization mode

  //CAN1->MCR = 0x51UL;                 // Hardware initialization(No automatic retransmission)
  CAN1->MCR = 0x41UL;                   // Hardware initialization(With automatic retransmission)
   
  // Set bit rates
  CAN1->BTR &= ~(((0x03) << 24) | ((0x07) << 20) | ((0x0F) << 16) | (0x1FF));
  CAN1->BTR |=  (((can_configs[bitrate].TS2-1) & 0x07) << 20) | (((can_configs[bitrate].TS1-1) & 0x0F) << 16) | ((can_configs[bitrate].BRP-1) & 0x1FF);

  // Configure Filters to default values
  CAN1->FMR  |=   0x1UL;                // Set to filter initialization mode
  CAN1->FMR  &= 0xFFFFC0FF;             // Clear CAN2 start bank

  // bxCAN has 28 filters.
  // These filters are used for both CAN1 and CAN2.
  // STM32F103 has only CAN1, so all 28 are used for CAN1
  CAN1->FMR  |= 0x1C << 8;              // Assign all filters to CAN1

  // Set fileter 0
  // Single 32-bit scale configuration
  // Two 32-bit registers of filter bank x are in Identifier Mask mode
  // Filter assigned to FIFO 0
  // Filter bank register to all 0
  CANSetFilter(0, 1, 0, 0, 0x0UL, 0x0UL);
 
  CAN1->FMR   &= ~(0x1UL);              // Deactivate initialization mode

  uint16_t TimeoutMilliseconds = 1000;
  bool can1 = false;
  CAN1->MCR   &= ~(0x1UL);              // Require CAN1 to normal mode

  // Wait for normal mode
  // If the connection is not correct, it will not return to normal mode.
  for (uint16_t wait_ack = 0; wait_ack < TimeoutMilliseconds; wait_ack++) {
    if ((CAN1->MSR & 0x1UL) == 0) {
      can1 = true;
      break;
    }
    delayMicroseconds(1000);
  }
  //Serial3.print("can1=");
  //Serial3.println(can1);
  if (can1) {
    Serial3.println("CAN1 initialize ok");
  } else {
    Serial3.println("CAN1 initialize fail!!");
    return false;
  }
  return true;
}


#define STM32_CAN_TIR_TXRQ              (1U << 0U)  // Bit 0: Transmit Mailbox Request
#define STM32_CAN_RIR_RTR               (1U << 1U)  // Bit 1: Remote Transmission Request
#define STM32_CAN_RIR_IDE               (1U << 2U)  // Bit 2: Identifier Extension
#define STM32_CAN_TIR_RTR               (1U << 1U)  // Bit 1: Remote Transmission Request
#define STM32_CAN_TIR_IDE               (1U << 2U)  // Bit 2: Identifier Extension

#define CAN_EXT_ID_MASK                 0x1FFFFFFFU
#define CAN_STD_ID_MASK                 0x000007FFU
 
/**
 * Decodes CAN messages from the data registers and populates a
 * CAN message struct with the data fields.
 *
 * @preconditions A valid CAN message is received
 * @params CAN_rx_msg - CAN message structure for reception
 *
 */
void CANReceive(CAN_msg_t* CAN_rx_msg)
{
  uint32_t id = CAN1->sFIFOMailBox[0].RIR;
  if ((id & STM32_CAN_RIR_IDE) == 0) { // Standard frame format
      CAN_rx_msg->format = STANDARD_FORMAT;;
      CAN_rx_msg->id = (CAN_STD_ID_MASK & (id >> 21U));
  }
  else {                               // Extended frame format
      CAN_rx_msg->format = EXTENDED_FORMAT;;
      CAN_rx_msg->id = (CAN_EXT_ID_MASK & (id >> 3U));
  }

  if ((id & STM32_CAN_RIR_RTR) == 0) { // Data frame
      CAN_rx_msg->type = DATA_FRAME;
  }
  else {                               // Remote frame
      CAN_rx_msg->type = REMOTE_FRAME;
  }

 
  CAN_rx_msg->len = (CAN1->sFIFOMailBox[0].RDTR) & 0xFUL;
 
  CAN_rx_msg->data[0] = 0xFFUL &  CAN1->sFIFOMailBox[0].RDLR;
  CAN_rx_msg->data[1] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDLR >> 8);
  CAN_rx_msg->data[2] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDLR >> 16);
  CAN_rx_msg->data[3] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDLR >> 24);
  CAN_rx_msg->data[4] = 0xFFUL &  CAN1->sFIFOMailBox[0].RDHR;
  CAN_rx_msg->data[5] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDHR >> 8);
  CAN_rx_msg->data[6] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDHR >> 16);
  CAN_rx_msg->data[7] = 0xFFUL & (CAN1->sFIFOMailBox[0].RDHR >> 24);

  // Release FIFO 0 output mailbox.
  // Make the next incoming message available.
  CAN1->RF0R |= 0x20UL;
}
 
/**
 * Encodes CAN messages using the CAN message struct and populates the
 * data registers with the sent.
 *
 * @params CAN_tx_msg - CAN message structure for transmission
 *
 */
void CANSend(CAN_msg_t* CAN_tx_msg)
{
  volatile int count = 0;

  uint32_t out = 0;
  if (CAN_tx_msg->format == EXTENDED_FORMAT) { // Extended frame format
      out = ((CAN_tx_msg->id & CAN_EXT_ID_MASK) << 3U) | STM32_CAN_TIR_IDE;
  }
  else {                                       // Standard frame format
      out = ((CAN_tx_msg->id & CAN_STD_ID_MASK) << 21U);
  }

  // Remote frame
  if (CAN_tx_msg->type == REMOTE_FRAME) {
      out |= STM32_CAN_TIR_RTR;
  }

  CAN1->sTxMailBox[0].TDTR &= ~(0xF);
  CAN1->sTxMailBox[0].TDTR |= CAN_tx_msg->len & 0xFUL;
 
  CAN1->sTxMailBox[0].TDLR  = (((uint32_t) CAN_tx_msg->data[3] << 24) |
                               ((uint32_t) CAN_tx_msg->data[2] << 16) |
                               ((uint32_t) CAN_tx_msg->data[1] <<  8) |
                               ((uint32_t) CAN_tx_msg->data[0]      ));
  CAN1->sTxMailBox[0].TDHR  = (((uint32_t) CAN_tx_msg->data[7] << 24) |
                               ((uint32_t) CAN_tx_msg->data[6] << 16) |
                               ((uint32_t) CAN_tx_msg->data[5] <<  8) |
                               ((uint32_t) CAN_tx_msg->data[4]      ));

  // Send Go
  CAN1->sTxMailBox[0].TIR = out | STM32_CAN_TIR_TXRQ;

  // Wait until the mailbox is empty
  while(CAN1->sTxMailBox[0].TIR & 0x1UL && count++ < 1000000);

  // The mailbox don't becomes empty while loop
  if (CAN1->sTxMailBox[0].TIR & 0x1UL) {
    Serial3.println("Send Fail");
    Serial3.println(CAN1->ESR);
    Serial3.println(CAN1->MSR);
    Serial3.println(CAN1->TSR);
  }
}

 /**
 * Returns whether there are CAN messages available.
 *
 * @returns If pending CAN messages are in the CAN controller
 *
 */
uint8_t CANMsgAvail(void)
{
  // Check for pending FIFO 0 messages
  return CAN1->RF0R & 0x3UL;
}


uint8_t counter = 0;
uint8_t counter1 = 0;
uint8_t frameLength = 0;
uint8_t frameLength1 = 0;
unsigned long previousMillis = 0;     // stores last time output was updated
unsigned long previousMillis1 = 0;
const long interval = 200;           // transmission interval (milliseconds)
const long interval1 = 200;

void setup() {
  Serial3.begin(115200);
  //bool ret = CANInit(CAN_500KBPS, 0);  // CAN_RX mapped to PA11, CAN_TX mapped to PA12
  bool ret = CANInit(CAN_125KBPS, 2);  // CAN_RX mapped to PB8, CAN_TX mapped to PB9
  //bool ret = CANInit(CAN_1000KBPS, 0);  // CAN_RX mapped to PA11, CAN_TX mapped to PA12
  //bool ret = CANInit(CAN_1000KBPS, 2);  // CAN_RX mapped to PB8, CAN_TX mapped to PB9
  //bool ret = CANInit(CAN_1000KBPS, 3);  // CAN_RX mapped to PD0, CAN_TX mapped to PD1
  if (!ret) while(true);
}

void loop() {
  RadiopwrON();
  delay(200);
  RadioID();
  delay(200);
  CANReceiveSerial();
}

void RadiopwrON()
{
  CAN_msg_t CAN_TX_msg1;
 
  CAN_TX_msg1.data[0] = 0x63;
  CAN_TX_msg1.data[1] = 0x00;
  CAN_TX_msg1.data[2] = 0x00;
  CAN_TX_msg1.data[3] = 0x00;
  CAN_TX_msg1.data[4] = 0x00;
  CAN_TX_msg1.data[5] = 0x00;
  CAN_TX_msg1.len = 6;
  //CAN_TX_msg1.len = frameLength1;

 

  unsigned long currentMillis1 = millis();
  if (currentMillis1 - previousMillis1 >= interval1) {
    previousMillis1 = currentMillis1;
    //if ( ( counter1 % 2) == 0) {
      //CAN_TX_msg1.type = DATA_FRAME;
      //if (CAN_TX_msg1.len == 0) CAN_TX_msg1.type = REMOTE_FRAME;
      //CAN_TX_msg1.format = STANDARD_FORMAT;
      CAN_TX_msg1.id = 0x000;
    //}
    CANSend(&CAN_TX_msg1);
    //frameLength1++;
    //if (frameLength1 == 7) frameLength1 = 0;
    //counter1++;
  }
}

void RadioID()
{
  CAN_msg_t CAN_TX_msg;

  CAN_TX_msg.data[0] = 0xFD;
  CAN_TX_msg.data[1] = 0x16;
  CAN_TX_msg.data[2] = 0x3F;
  CAN_TX_msg.data[3] = 0xFF;
  CAN_TX_msg.data[4] = 0xFF;
  CAN_TX_msg.data[5] = 0xFF;
  CAN_TX_msg.data[6] = 0xFF;
  CAN_TX_msg.data[7] = 0xFF;
  CAN_TX_msg.len = 8;
  //CAN_TX_msg.len = frameLength;

 

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    //if ( ( counter % 2) == 0) {
      //CAN_TX_msg.type = DATA_FRAME;
      //if (CAN_TX_msg.len == 0) CAN_TX_msg.type = REMOTE_FRAME;
      //CAN_TX_msg.format = STANDARD_FORMAT;
      CAN_TX_msg.id = 0x415;
    //}
    CANSend(&CAN_TX_msg);
    //frameLength++;
    //if (frameLength == 9) frameLength = 0;
   //counter++;
  }
}

void CANReceiveSerial()
{
  CAN_msg_t CAN_RX_msg;
  if(CANMsgAvail()) {
    CANReceive(&CAN_RX_msg);

    if (CAN_RX_msg.format == EXTENDED_FORMAT) {
      Serial3.print("Extended ID: 0x");
      if (CAN_RX_msg.id < 0x10000000) Serial3.print("0");
      if (CAN_RX_msg.id < 0x1000000) Serial3.print("00");
      if (CAN_RX_msg.id < 0x100000) Serial3.print("000");
      if (CAN_RX_msg.id < 0x10000) Serial3.print("0000");
      Serial3.print(CAN_RX_msg.id, HEX);
    } else {
      Serial3.print("Standard ID: 0x");
      if (CAN_RX_msg.id < 0x100) Serial3.print("0");
      if (CAN_RX_msg.id < 0x10) Serial3.print("00");
      Serial3.print(CAN_RX_msg.id, HEX);
      Serial3.print("     ");
    }

    Serial3.print(" DLC: ");
    Serial3.print(CAN_RX_msg.len);
    if (CAN_RX_msg.type == DATA_FRAME) {
      Serial3.print(" Data: ");
      for(int i=0; i<CAN_RX_msg.len; i++) {
        Serial3.print("0x");
        Serial3.print(CAN_RX_msg.data[i], HEX);
        if (i != (CAN_RX_msg.len-1))  Serial3.print(" ");
      }
      Serial3.println();
    } else {
      Serial3.println(" Data: REMOTE REQUEST FRAME");
    }
  }
}
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
Well I found by pressing on the main FPGA chip in the unit that I was able to influence if the illumination came on or not.  So I tried to reflow it with one of those Chinese hot air stations but I think I may have done more damage then good as the current draw is now around 1.4A and no lights.  I was not able to get the chip hot enough to even move a little even at 430C and 6 air flow.
 

Offline james_s

  • Super Contributor
  • ***
  • Posts: 21611
  • Country: us
If you roasted the IC at 430C you probably destroyed it. You really should not be trying to reflow BGAs unless you know what you're doing, and certainly you should practice on some scrap rather than on the device you're trying to repair.
 

Offline SilverSolder

  • Super Contributor
  • ***
  • Posts: 6126
  • Country: 00
If you roasted the IC at 430C you probably destroyed it. You really should not be trying to reflow BGAs unless you know what you're doing, and certainly you should practice on some scrap rather than on the device you're trying to repair.

Come on, @james_s....   Live a little!  :D
 

Offline james_s

  • Super Contributor
  • ***
  • Posts: 21611
  • Country: us
Come on, @james_s....   Live a little!  :D

If there's nothing to lose, then sure, go for it, but in this case:

- Pushing on the IC caused a change in behavior, very strongly suggesting that the problem (or at least a problem) was solder joints.

- There are people out there such as Rossman who are highly skilled at fixing problems of this nature and can do so with minimal risk of additional damage.

- This was a potentially expensive device that belongs to someone else, who entrusted the OP to work on it, probably hoping they could fix it, or at least know when to stop and not make it worse.


Chalk this up as a learning experience. We've all looked back at some point and wished we'd realized we were in over our head and known when to quit before making something worse, but it still sucks. Hopefully the owner of the radio has the attitude that it was already broken and had already decided that it was not worth paying an experienced technician to repair it so nothing to lose.
 
The following users thanked this post: SilverSolder

Offline dc101

  • Regular Contributor
  • *
  • Posts: 220
  • Country: us
Well I found by pressing on the main FPGA chip in the unit that I was able to influence if the illumination came on or not.  So I tried to reflow it with one of those Chinese hot air stations but I think I may have done more damage then good as the current draw is now around 1.4A and no lights.  I was not able to get the chip hot enough to even move a little even at 430C and 6 air flow.

For heavy boards with large ground planes, lots of layers etc... you really should invest in a board pre-heater for hot-air rework.

It's hard to say what happened, the chip could be dead, some of the solder balls may have melted and bridged  :-//
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
I have used 430C on Macbook motherboards before to remove and replace smaller BGA chips with out issue.  One Youtuber I watch uses 480C! and they do not have any issues.  even the small SMD resistors and caps did not move so I doubt that I got it hot enough to cause damage but I am not sure.  I do know that the flux I use can sometimes be partially conductive so I have now cleaned that up as best as possible and am letting it dry in the toaster oven at 60C.
 

Offline james_s

  • Super Contributor
  • ***
  • Posts: 21611
  • Country: us
Well the fact that people get away with it sometimes doesn't mean it's a good idea. As someone already mentioned, with something like this you'd typically need to preheat the board, usually somewhere around 100C and then once it's all good and toasty you use hot air to heat the part you're trying to rework enough to finish the job.

Unfortunately I suspect that you're going to have trouble finding a pro willing to touch it after making the problem worse with a repair attempt although you've got nothing to lose by trying. If it is indeed an FPGA then it should be possible to replace it, most FPGAs are programmed at power-up by an external configuration ROM so you can replace them with a blank part of the same type.
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
Well cleaning the flux and with ISO and drying it out did not help.  The chip is a Xilinx Spartan xa3s1500 so it looks like it does load its configuration from a external chip so that is good.  However it costs over $250 so I think that is off the table.  Oh well.  I also told the coworker the risks of trying to reflow the chip and they were ok with the potential of it getting worse.  It was very intermittent to start with and more often then not would not work at all.
« Last Edit: May 28, 2021, 03:07:12 am by poot36 »
 

Offline james_s

  • Super Contributor
  • ***
  • Posts: 21611
  • Country: us
Wow that's a really big FPGA, it's the top of the Spartan3 family IIRC.

$250 where? FPGA prices are really weird, if you go and buy one from Digikey or whatever you can pay a fortune, sometimes more than it costs to find an entire dev board from China. Given the reusable nature of FPGAs you might find some totally unrelated equipment that uses the same part, the Spartan3 is an older series, it was popular maybe 10-15 years ago so a device that has one might not cost a fortune, or you might find some NOS parts on ebay. Nobody is going to design new equipment around such an old part so demand will be limited to someone who just happens to need that specific chip to fix something.
 

Offline dc101

  • Regular Contributor
  • *
  • Posts: 220
  • Country: us
I think the hard part will be trying to find something that uses the same Spartan 3 automotive series. There's still plenty of places trying to offload their ancient Spartan 3 starter kits, like Digilent, but again those are completely different versions of the chip.
 

Offline james_s

  • Super Contributor
  • ***
  • Posts: 21611
  • Country: us
Isn't the automotive range the same part, just certified over a wider temperature range? I'm guessing they just bin them according to demand. A non-automotive part won't be guaranteed over the whole temperature range but that doesn't mean it won't work. It's just a radio, not something critical like an ECU or airbag controller. I'm surprised there's an FPGA in there in the first place.
 

Offline dc101

  • Regular Contributor
  • *
  • Posts: 220
  • Country: us
I honestly don't know what the differences are in the automotive series, but I would suspect it's more than temp binning. The industrial 3E is rated 100C which is the standard temp for the automotive series. I didn't read the entire datasheet for the automotive series, but at a quick glance it seems the main differences are the FPGA attributes, system gates, logic cells, user io etc... the XA3S1500 has 1.5M logic gates compared to the XC3S200E (Xilinx Spartan 3E Starter Kit) with 200K logic gates. To your point though, assuming you could find a non-automative series FPGA that had the appropriate attributes, I think you're correct in saying it should work.
« Last Edit: May 28, 2021, 05:03:51 am by dc101 »
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
Here are some inside shots of the unit, sorry about the low quality the lighting is not very good.
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
Overall shot.  This unit has GPS, Nav, a HDD, supports DVD playback and backup camera so it would need a powerful processor to do that.  It even has a DSP chip as well.  You can copy 2000 songs to the internal HDD from CD's as well.  It supports MP3, WMA, JPEG files and has Bluetooth for your cellphone with voice activated commands and Sirius radio.  There are 3 RF connectors on the back of the unit.  No anti-theft function though which I found a bit odd.  I am going to try and figure out how to rig up a board preheater and then use my heat gun at around the 350C to 400C range and see if I can get the FPGA to move slightly (I hope...).
« Last Edit: May 29, 2021, 01:57:07 am by poot36 »
 

Offline james_s

  • Super Contributor
  • ***
  • Posts: 21611
  • Country: us
Why do you want the FPGA to move? Positioning is absolutely critical for a fine pitch BGA part like that, you don't want it to move, really it needs to be removed completely, re-balled and soldered back in place. I would really suggest sending it out to someone who is experienced at doing this before you completely destroy the PCB. Your chance of success doing it yourself at this point is pretty close to zero.
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
@james_s I just want it to move a very slight amount and then snap back into place to verify that I have melted all the balls on the bottom.  I have seen this technique online before.  I may end up taking it off anyways as I may have bridged some solder balls already.  I am still learning that is for sure but if you do not try you will never know what you can do.  Even if I wanted to send it out for a re-ball I have no clue who or where I would send it as in my neck of the woods trying to find some place that repairs generic electronics is very hard to find.
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
Well I used an old electric frying pan as a preheater and got the board up to around 70C to 80C or so and used 400C and airflow 8 on my heat gun with no nozzle and I was able to get the chip to move like I wanted.  However now the current draw is even less (I did not have one of the RF boards connected so that could explain it) so I assume that I will have to take the chip off the board to inspect it.  I also found out that there is another large ST std5700hue (have not been able to find a datasheet for it) part on the under side of the PCB that is slightly overlapping with the FPGA on the top of the board so that chip is also suspect as well.  This is turning out to be an interesting repair attempt to say the least.
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
Just ordered BGA stencils and some 0.6mm BGA balls as well as some (probably fake) Kapton tape online.  Just have to wait for it to come in and then see what I can do.
 

Offline SilverSolder

  • Super Contributor
  • ***
  • Posts: 6126
  • Country: 00

Beginning to sound like a bit of an adventure!  :D

Obviously only by trying it, will you be able to figure this out...
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
I just hope I ordered the correct size of solder balls as the data sheet for the chip was not very clear on the size.
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
The solder balls just came in today and man are they tiny, 0.6mm is very small.  Going to be fun placing them when the stencil comes in.  Good thing there are 25000 in the small bottle they came in.
 

Offline SilverSolder

  • Super Contributor
  • ***
  • Posts: 6126
  • Country: 00

How do they actually get placed - do you just drizzle them over the stencil, basically?
 

Offline dc101

  • Regular Contributor
  • *
  • Posts: 220
  • Country: us
Flux the chip, secure the stencil and then yes exactly, sprinkle the solder balls over the chip (usually over another container to catch the excess). smooth with a flat tool, check for missing solder balls and then use a little hot air to slightly melt the solder balls to secure them in place. remove stencil.
 
The following users thanked this post: SilverSolder

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
@dc101 I saw that technique on YouTube.  Will give it a try and see how it goes.  Also got in the stencils today so just waiting on the Kapton tape to arrive and this heat wave to go away and I will be able to give it a go.
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
So I finally got in all the parts to give the re-ball a try and it went went wrong in ways that I would have not imagined, the stencil got stuck to the chip and the only way to get it off was to heat up the solder again and pull it off!  This did make most of the balls that I had placed turn into pointy little peaks so I will have to do the re-ball all over again.  The stencil that I used had 0.6mm holes in in and did line up correctly with the pads on the chip.  I also used 0.6mm solder balls, is that the wrong thing to do?  Here are the steps I did to re-ball the chip.  Cleaned all the remaining solder balls of the removed chip using solder wick and flux and made it as flat as I could, added a thin layer of flux to the chip, placed stencil on chip and secured down with Krapton tape, poured solder balls onto the stencil and moved them around until all the holes were filled, re-flowed the solder balls with the hot air, attempted to take off the stencil only to find out it would not come off and that some of the solder appears to have stuck to the stencil.  Should I have taken off the stencil after I placed the solder balls and then re-flowed it with hot air at a low air flow setting or did I not get the chip hot enough with the hot air as when I did mange to take off the stencil with the hot air the ground and power pads were the ones that turned into peaks indicating not enough heat to flow it into a ball?  Do I need to preheat the chip before using the hot air on it to flow the solder balls?  This has turned into a interesting learning experience.
« Last Edit: July 11, 2021, 08:49:11 pm by poot36 »
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
Well I think it may be screwed, I inspected the FPGA chip and found a missing pad that relates to a I/O pin on the chip.  I have also confirmed that it is connected to something else in the radio.  Not sure what it does but I wonder if it was the original cause of the radio not powering on all the time.
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
Well I cleaned the excess flux off the chip and found that the pad is still there so it was just the flux covering it that had turned brown with the heat making it look like I was missing a pad.  It is game on again.
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
Ok, I tired taking the stencil off after I had placed the balls with it and the balls went everywhere.  Is there a right way to do this?  Should I flux the chip, place the stencil and balls and then heat the chip from the underside with the electric frying pan until the balls melt?
 

Offline poot36Topic starter

  • Frequent Contributor
  • **
  • Posts: 678
  • Country: ca
Well I managed to get the chip re-balled with the stencil and some solder past and some help from a friend who also had a proper pre-heater.  Installed the chip back onto the radio PCB and it now draws around 0.95A and still does not power up but that does change if I power it up with putting pressure on one side of the chip so I think that the reflow did not work correctly or I damaged the chip taking it off.  I think the only way this is getting fixed is by a pro and that would exceed the cost of getting a replacement used radio.  At least I tried.  Thanks for the help.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf