CAN on ATSAMC21J18A - Page 1
Forum section: electronics / general

author: ziehao
author_rank: Newbie
timestamp: June 18, 2019, 08:45:38 am
content: I am learning how to program a ATSAMC21J18A MCU, and so far, I have managed to properly write an I2C initialization and Read/Write Transmission code on theregister level, things like below...SERCOM5->I2CM.ADDR.reg = 0x16;SERCOM5->I2CM.DATA.reg = 0x44;I am now trying to do a CAN Extended ID transmit and receive by writing the code on a register level as well, things like below...CAN0->CCCR.reg |= CAN_CCCR_INIT;CAN0->XIDFC.bit.LSE = 0x00;So now, I have roughly wrote the CAN initialization code, but the datasheet does not say clearly which registers should I put the Extended Address for Receiving messages, or also where should I put the data for Transmitting.I am lost on the Tx/Rx FIFO and Message RAM as well, where to put the Address and Data etc.Could someone guide me on the steps involved or a certain link for a tutorial on this topic?Thanks in advance...

author: JPortici
author_rank: Super Contributor
timestamp: June 18, 2019, 09:38:25 am
content: When i was evaluating the C21 i had some trouble at first. I opened an ASF example project using CAN and i single stepped every function of interest until i found what i needed (i HATE ATSAM datasheets. They are the equivalent of autogenerated doxygen crap)It was just a test though, i quickly went back to PIC32MX, but it didn't require much work

author: KIKi
author_rank: Newbie
timestamp: June 18, 2019, 09:57:57 am
content: Hi,You should use Atmel Start Frameworkhttps://start.atmel.com/. There is a bug in CAN driver of ASF. ASF driver is not setting proper ACK bit when CAN message is received. You should change_can_async_readfunction (hpl_can.c) as it is shown below (marked with commentFix). Everything else should work as configured in Atmel Start. Be careful with clocks and sampling segments, that is most important thing in CAN!Code:int32_t _can_async_read(struct _can_async_device *const dev, struct can_message *msg){struct _can_rx_fifo_entry *f = NULL;uint32_t pTemp = 0; // Fix.if (!hri_can_read_RXF0S_F0FL_bf(dev->hw)) {return ERR_NOT_FOUND;}#ifdef CONF_CAN0_ENABLEDif (dev->hw == CAN0) {f = (struct _can_rx_fifo_entry *)(can0_rx_fifo + hri_can_read_RXF0S_F0GI_bf(dev->hw) * CONF_CAN0_F0DS);}#endif#ifdef CONF_CAN1_ENABLEDif (dev->hw == CAN1) {f = (struct _can_rx_fifo_entry *)(can1_rx_fifo + hri_can_read_RXF0S_F0GI_bf(dev->hw) * CONF_CAN1_F0DS);}#endifif (f == NULL) {return ERR_NO_RESOURCE;}if (f->R0.bit.XTD == 1) {msg->fmt = CAN_FMT_EXTID;msg->id = f->R0.bit.ID;} else {msg->fmt = CAN_FMT_STDID;/* A standard identifier is stored into ID[28:18] */msg->id = f->R0.bit.ID >> 18;}if (f->R0.bit.RTR == 1) {msg->type = CAN_TYPE_REMOTE;}const uint8_t dlc2len[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64};msg->len = dlc2len[f->R1.bit.DLC];memcpy(msg->data, f->data, msg->len);// Get FIFI0 index.pTemp = hri_can_read_RXF0S_F0GI_bf(dev->hw); // Fix.// Ack.hri_can_write_RXF0A_reg(dev->hw, pTemp); // Fix.return ERR_NONE;}

author: ataradov
author_rank: Super Contributor
timestamp: June 18, 2019, 04:58:51 pm
content: Here is a simple bare-metal CAN bootloader that shows basic use of that peripheral.The CAN IP in this device is made by Bosch, so you can look up the Bosch datasheet (it is public), it may have a bit more information.

author: ziehao
author_rank: Newbie
timestamp: June 19, 2019, 01:18:20 am
content: Hi JPortici,I know right... SAM datasheets are so hard to understand sometimes... AVR datasheets are good though.Hi KIKi,Thanks for the reply, but I wish to do the CAN programming the Register Way (as some would call it) instead of using the ASF. Mainly because the ASF is hard to understand and/or customise.I have written some I2C codes using the Register Way, and it had problems merging with the CAN ASF version, that is also one of the reason I do not wish to use the ASF...Hi ataradov,Thanks for the file. You have no idea how long I have been looking for something like this.... (Maybe my googling skills are not there yet)I will look through it and update here again.

author: ziehao
author_rank: Newbie
timestamp: June 20, 2019, 10:00:56 am
content: I am trying to take out pieces of the Bosch codes to do a simple Tx and Rx.Looking at the Bosch codes, I am confused about the below sectionCode:static alignas(4) CanMramSidfe can_rx_standard_filter;static alignas(4) CanMramXifde can_rx_Xstandard_filter;static alignas(4) CanMramRxbe can_rx_buffer;static alignas(4) CanMramTxbe can_tx_buffer;static alignas(4) CanMramTxefe can_tx_event_fifo;What is the use of aligns(4), do I need to copy the .h files to my main program?If I do not need to use it, what alternative way should I write it?

author: ataradov
author_rank: Super Contributor
timestamp: June 20, 2019, 05:05:55 pm
content: Just to be clear, the code is not from Bosch, it is from Microchip. The code was written by me.alignas() is a standard C directive to ensure that variables are aligned in the memory in a certain way. In this case hardware expects those things to be aligned at a 32-bit boundary (4 bytes).I don't really understand the rest of the question. The code presented here is essentially minimal working code. You need to have everything related to CAN for CAN to work.It is hard to say how you should integrate this into your application without knowing the details of that application.

author: L1L1
author_rank: Regular Contributor
timestamp: June 21, 2019, 01:43:49 pm
content: Quote from: ataradov on June 20, 2019, 05:05:55 pmThanks for this code, I was just looking into the SAMC21 CAN peripheral a few weeks ago myself.I often look at your code on Github (https://github.com/ataradov/mcu-starter-projects) for ideas. It's been helpful.You should tell Microchip that they need to improve their documentation. The section on CAN bus in the SAMC21 datasheet is terrible.

author: JPortici
author_rank: Super Contributor
timestamp: June 21, 2019, 03:20:18 pm
content: Any section in the ATSAM datasheet is terrible. And unfortunately since microhip acquired atmel the 8bit team has started doing documentation Atmel style..The old datasheet would fill in 300 pages, the new ones require 450+ pages.tables that did fit in one page now need two.The same exact shit in the erratas. One of the few erratas styles that were concise, to the point, full of examples are being replaced by the wall of text that is on atmel errata.but hey, more color, more fonts, more fun!

author: ataradov
author_rank: Super Contributor
timestamp: June 21, 2019, 03:49:32 pm
content: Complain to support about this. There is nothing I personally can do about the documentation. All I can do is to struggle through it and create software that helps people understand how hardware works. And that's what I do.

author: ziehao
author_rank: Newbie
timestamp: June 28, 2019, 06:49:59 am
content: Hi,So I tried using the Atmel Start to generate some CAN code for me to test. The initialization was fine, now I can Transmit and Receive message.The CAN0_Handler in the hpl_can.c as shown belowCode:void CAN0_Handler(void){struct _can_async_device *dev = _can0_dev;uint32_t ir;ir = hri_can_read_IR_reg(dev->hw);if (ir & CAN_IR_RF0N) {// Rx New Messagedev->cb.rx_done(dev);PORT->Group.OUTTGL.reg |= PORT_PA15;// Toggle LED}if (ir & CAN_IR_TC) {// Timestamp Completeddev->cb.tx_done(dev);}if (ir & CAN_IR_BO) {// Bus Offdev->cb.irq_handler(dev, CAN_IRQ_BO);}if (ir & CAN_IR_EW) {// Error Warningdev->cb.irq_handler(dev, CAN_IRQ_EW);}if (ir & CAN_IR_EP) {// Error Passivedev->cb.irq_handler(dev, hri_can_get_PSR_EP_bit(dev->hw) ? CAN_IRQ_EP : CAN_IRQ_EA);}if (ir & CAN_IR_RF0L) {// Rx Message Lostdev->cb.irq_handler(dev, CAN_IRQ_DO);}hri_can_write_IR_reg(dev->hw, ir);// Clear Interrupt}Q1. I placed a toggle LED code so that I will know whenever it successfully receive a message, but I do not understand the linedev->cb.rx_done(dev);in the Rx New MessageCan someone summarize what is it about and what does it do?Q2. I am trying to save the Received message into a buffer for the Main code to use,ANDif the Received message has been saved into a buffer, how do I use it in the Main code.

author: ataradov
author_rank: Super Contributor
timestamp: June 28, 2019, 04:14:11 pm
content: If you want to work with ASF, then you should use ASF APIs. In the example code you will see how to set callbacks using functions like can_async_register_callback(). The IRQ handler will call the callback you have provided.After that you can read the data using can_async_read() function. All of that will happen in the main code. How to structure all of this is a matter of taste.But there is no need for interrupts if your main code just checks for a flag. It might as well just check for a flag in the CAN register and do what mu code does.

author: ziehao
author_rank: Newbie
timestamp: July 11, 2019, 06:57:30 am
content: So, just a solution for future ppl who are using CAN for ATSAMC21J, below are sections of the code that im using for CAN tx and rx with the help of Atmel StartCode:void CAN_tx(uint32_t tx_address, uint8_t tx_message) {struct can_message msg;struct can_filter filter;//insert code to copy received message into tx_message_2if (rtr == 1){/*Send extended message with ID: rtr_id and 8 byte data 0 to 7*/msg.id = rtr_id;msg.type = CAN_TYPE_DATA;//msg.data = rx_data;msg.data = tx_message;msg.len = 8;msg.fmt = CAN_FMT_EXTID;can_async_register_callback(&CAN_0, CAN_ASYNC_TX_CB, (FUNC_PTR)CAN_0_tx_callback);can_async_enable(&CAN_0);can_async_write(&CAN_0, &msg);button_pressed = 0;rtr = 0;}else{/*Send extended message with ID: 0x155555A5 and 8 byte data rx_data*/msg.id = tx_address;msg.type = CAN_TYPE_DATA;msg.data = rx_data;//msg.data = tx_message_2;msg.len = 8;msg.fmt = CAN_FMT_EXTID;can_async_register_callback(&CAN_0, CAN_ASYNC_TX_CB, (FUNC_PTR)CAN_0_tx_callback);can_async_enable(&CAN_0);can_async_write(&CAN_0, &msg);button_pressed = 0;}}void CAN0_Handler(void){volatile uint32_t status, interrupt;status = CAN0->IR.reg;interrupt = CAN0->IR.reg;if (interrupt & CAN_IR_RF0N) { // Rx New Messagereceived_msg = 1;PORT->Group.OUTTGL.reg |= PORT_PA15; // Toggle LEDCAN0->IR.bit.RF0N = 1; // Reset Interrupt}}mainCode:while (1){if (received_msg == 1){int buffer_index = CAN0->RXF0S.bit.F0GI;CAN_0_rx_callback(&CAN_0);// save data into rx_dataCAN0->RXF0A.bit.F0AI = buffer_index;received_msg = 0;button_pressed = 1;//PORT->Group.OUTTGL.reg |= PORT_PA15;// Toggle LED}if (button_pressed == 1) // or when message received{CAN_tx(0x155555A5, tx_message_2);button_pressed = 0;}}

author: jars121
author_rank: Regular Contributor
timestamp: October 21, 2020, 02:32:37 am
content: Sorry for bumping this thread, but there's some pertinent content here regarding CAN, SAM devices and ASF.Ataradov, thank you for providing the code example and the support, it's greatly appreciated. Are you aware of a similar code example for CAN on the SAME70? Failing that, I've not used the SAMC21, so I'm not sure how closely aligned it is to the SAME70, but I might try and port it across. I've tried my best to get a simple CAN RX example working on the SAME70 using the ASF SAMV71 example project but haven't had any luck.

author: ataradov
author_rank: Super Contributor
timestamp: October 21, 2020, 02:35:14 am
content: SAM E70 uses exactly the same Bosch CAN controller IP. The same code will run as is, with possible minor renaming of registers and obviously different clock configurations.The only thing to watch out for is that Bosch controller only has access to the 64 K of RAM, so all the pointers to the buffers you give to the IP must be located in the first 64 K of RAM.

author: jars121
author_rank: Regular Contributor
timestamp: October 21, 2020, 11:02:32 pm
content: Thanks for clarifying Alex, much appreciated.I've put considerable time into MCAN on the SAME70 using ASF and am really not getting anywhere. I'll start a new thread so as not to pollute this one any further.

author: chayma
author_rank: Newbie
timestamp: February 26, 2021, 08:47:06 am
content: Hello,I appreciate your help for us who struggle with CAN in asf4But your example didn't work in my code.Could you tell me your clock configuration.best regards,

author: ataradov
author_rank: Super Contributor
timestamp: February 26, 2021, 06:24:23 pm
content: The code for the bootloader I provided is complete and functional, it has all the clocks configurations. I have no idea about ASF4.

author: matti.salo
author_rank: Contributor
timestamp: October 07, 2021, 12:59:14 pm
content: Hi,I'm quite new with samc21. I have an custom board with samc21g18. I'm trying to get both CAN's to work with mcp2551. I have so far got the uart work with ASF4 and "testled" blinking. But for some reason i CAN's just won't work. First of all. ASF code seems to hang if i'm trying to use 48MHz main clock. Code does not hang if i divide it to 24MHz.Second, what ever divider / timequota settings i try the can signal is just a mess. It seems that CAN0 goes in to endless loop of crap if i try to send somethin with it.I have tried to unattach tranciever from CAN1 but still i dont get any sense to this.As far as i understand there should not be any problems in schematics sinse there is only cantx and canrx lines going into transiever.Can0 Scope:CAN1 Scope:Serial :Any working example that i could try on my hardware? just something that would sent test message/messages to CAN busses?I wasn't able to get that bootloader code to open and test in microchip studio 7.

author: ataradov
author_rank: Super Contributor
timestamp: October 07, 2021, 06:11:41 pm
content: Most likely reason for the hanging is incorrect setting for the flash wait states. ASF4 example CAN project is known to work out of the box.And as far the bootloader, it should also open in Atmel/Microchip studio. So error description may be helpful.You should probably post your schematics and full setup description. CAN can't have just one node, it will not work. You need to have at least two devices on the bus.

author: matti.salo
author_rank: Contributor
timestamp: October 14, 2021, 07:57:41 am
content: I didn't find "basic CAN" example only FD, bnut that doesn't help me since i do not have a another FD device.I have now again gathered example with ASF4 configurator:Clocks:CAN0:The main code is only:Code:#include <atmel_start.h>int main(void){/* Initializes MCU, drivers and middleware */atmel_start_init();delay_ms(1000);/* Replace with your application code */while (1) {CAN_0_example();USART_0_example();delay_ms(200);gpio_toggle_pin_level(LEDI);}}The CAN part of my board:In CAN bus i'm now using PCAN interface as a second device and Picoscope as debug device.With these settings i get:If i set just a random time quanta settinks like:25+25+25:What am i'm missing.

author: matti.salo
author_rank: Contributor
timestamp: October 14, 2021, 12:53:22 pm
content: Just after last message i got this to work.First: 48Mhz clock started to work after adding NVM waits from 0 to 1. thanks to this post:https://www.avrfreaks.net/forum/samc21-not-running-48mhz-and-96mhzSecond i added 120ohm resistor to can.Working setting on ASF are now:

author: ataradov
author_rank: Super Contributor
timestamp: October 14, 2021, 01:10:31 pm
content: I mentioned flash wait states in my original reply.Example in ASF3 is much better and has both FD and classical example in the same application controllable though serial port menus. Although it looks like that CAN FD demo from ASF4 is the same exact demo.

author: matti.salo
author_rank: Contributor
timestamp: October 15, 2021, 08:19:25 am
content: Yes, you did mention it, but i did not understand itso that was my bad. Configuring clocks is new to me.I thank you ataradov, without your help i would not got this to work.

author: Rudolph Riedel
author_rank: Regular Contributor
timestamp: October 23, 2021, 02:55:53 pm
content: Quote from: matti.salo on October 14, 2021, 12:53:22 pmIt might work but as the datasheet and the quote in the linked thread says, for 48MHz the SAMC21 needs 2 wait-states, not 1.Unfortunately Atmel never fully qualified what the ATSAMC2x or ATSAMx5x are capable of and Microchip does not seem to care,so a lot of these numbers are still based on simulation rather than qualified measurements and so a lot of these Maximum frequency numbers for the various peripheral units look way too conservative.But if the datasheet states that 48MHz require 2WS I rather configure the controller to 2WS than risking the software to fail in some random way that is impossible to reproduce.And in this context I wonder why the same CAN-FD module supposedly can only do 48MHz in the ATSAMC21, 100MHz in the ATSAME51 and whatever the limit is for ATSAMx7x as it looks like this information is not given in the datasheet.

author: ataradov
author_rank: Super Contributor
timestamp: October 23, 2021, 04:00:48 pm
content: Quote from: Rudolph Riedel on October 23, 2021, 02:55:53 pmIt just seems they are conservative. When things are specified based on simulation, there is a great level of confidence that real characterization would not improve them by a lot.Quote from: Rudolph Riedel on October 23, 2021, 02:55:53 pm48 MHz is the maximum frequency for C21. 100 MHz is the typical maximum value for a lot of peripherals that don't need higher clock for D5x/E5x. Having lower value (less than 120 MHz) means that it is easier to meet timing requirements for routing. This is the same reason for introduction of I/O Sets. Too many pin multiplexing options make it very hard to meet timing requirements.For V7x there are recommended frequencies: "It is recommended to use the CAN clock at frequencies of 20, 40 or 80 MHz." Those are easy to derive from standard PLLs and there is no need for CAN to have a higher clock for real applications.

author: Rudolph Riedel
author_rank: Regular Contributor
timestamp: October 24, 2021, 10:59:03 am
content: Quote from: ataradov on October 23, 2021, 04:00:48 pmWell, the C21 has only one PLL and the x5x only has two PLLs and the GCLK only allow even dividers.So with the given values you are severly limited.That would change a little if in the x51 SERCOM, CAN, TC4...TC7, AC, ADC and DAC at least had a maximum input frequency of 120MHz.And if the CAN-FD in the C21 would allow 80MHz it would work with more than 2Mbit/s, the "10 Mb/s for CAN-FD mode" as stated in the datasheet are not possible with only 48MHz, at least not reliable.8Mbit/s would be 6tq at 48MHz and 5tq at 40MHz, 10tq at 80MHz and 15tq at 120MHz.This alone should be a valid reason to actually qualify what the controllers really can do.Quote from: ataradov on October 23, 2021, 04:00:48 pmWell, how do you get 40MHz clock for the CAN in a C21 and 48MHz for the core? You can't.Likewise you need two PLLs to use 80MHz for the CAN in an E51 while running the core with 120MHz since the PLL and the GCLK can "only" do 200MHz and not 240MHz.And then you have OEM requirements that CAN-FD controllers must use a clock with multiples of 40MHz.So you either violate that requirement for the C21 or clock the core at 40MHz as well.And for E51 you practically have to use both PLLs to run the CAN-FD at 80MHz and the core at 120MHz which means that every other peripheral is locked into that base frequencies as well. Running either the CAN-FD at 120MHz or the core at 80MHz or 160MHz would free the second PLL.Do I need to use 80MHz or 120MHz for the CAN-FD? Yes, 40MHz only works up to 2Mbit/s.

author: ataradov
author_rank: Super Contributor
timestamp: October 24, 2021, 04:25:43 pm
content: Quote from: Rudolph Riedel on October 24, 2021, 10:59:03 amUse PLL for 40 MHz and RC oscillator for the rest of the system. 48 MHz RC oscillator is plenty accurate to run the code, and you can derive the clock for other peripherals from the same PLL.Quote from: Rudolph Riedel on October 24, 2021, 10:59:03 amThat's why you have 2 PLLs.That 100 vs 120 MHz will not characterize. I've seen those devices fail when overclocked like this. So there is not a whole lot of margin there.

author: matti.salo
author_rank: Contributor
timestamp: August 02, 2022, 06:08:20 pm
content: Again i jumped to a weird thing. I have been able to configure the CAN controller to work with 500kbit/s and i can successfully read it wit PCan cable and also with picoscope oscilloscope. I have MCP2551tranciever. But.... when i'm connecting it to my other board that has sam3x8e/SN65HVD230 it cannot get the data. after disconnecting/connecting the can cable several times ( 5-30) times it just some time starts to work. With oscilloscope i have checked that one bit is just the 2uS long as it should be with the 500kbps speed if i have understood right. Any idea what is going wrong? what to look in next step? canbus resistors have been tried few different sizes.the other 3x8e board we have been using to read can data from other 500kbps devices so there should not be a problems.

author: ataradov
author_rank: Super Contributor
timestamp: August 02, 2022, 06:17:38 pm
content: You need to be more specific. A common thing to forget is ground connection, for example. You may have incorrectly configured the sampling points, for example. What is your configuration? Have you tried to change it?Connecting/disconnecting the cables does nothing without actual data transfer. So, do you actively transfer frames while doing this?To verify the baudrate setting, it is much better to transmit the data from the device.Attach scope traces of what you see on the bus.

author: matti.salo
author_rank: Contributor
timestamp: August 02, 2022, 07:21:50 pm
content: The devices are sharing a common ground since they are powered from same power supply, so that should not be a problem.at sending device samc21g18 i have following sampling configuration with 16MHz clock:Code:CONF_CAN0_BTP_BRP 2CONF_CAN0_BTP_TSEG1 13CONF_CAN0_BTP_TSEG2 2CONF_CAN0_BTP_SJW 2CONF_CAN0_DBTP_TDC 0CONF_CAN0_DBTP_DBRP 2CONF_CAN0_DBTP_DTSEG1 10CONF_CAN0_DBTP_DTSEG2 3CONF_CAN0_DBTP_DSJW 3At the sam3x8e i have used asf3 and command to initialize canbus:Code:can_init(CAN0, ul_sysclk, CAN_BPS_500K);The code at the sending c21 has a 40ms timer that acively sends a can packet and that i can verify with scope:I have tried to change a bit timing settings but i cannot make a difference. but i have to admit that i'm not a pro to calculate them

author: eutectique
author_rank: Frequent Contributor
timestamp: August 02, 2022, 10:39:37 pm
content: 1. If the bit time is indeed 1.92 μs, then your baud rate is about 521 kbps, or 4.17% off. This is way too much.2. The offset of ACK bit and floating level of EOF looks suspicious.3. Some time ago I evaluated CAN on SAMC21 xplained board and, afair, it worked OK. The other side was TI's TCAN4550. Cable length was about 30 cm with 120 ohm termination on TCAN.I don't have the SAMC21 board handy, but here is my configuration of CAN0:Code:#define CONF_CAN0_CCCR_FDOE 1#define CONF_CAN0_CCCR_BRSE 1#define CONF_CAN0_MRCFG_RUNSTANDBY 0#define CONF_CAN0_MRCFG_DQOS 2#define CONF_CAN0_BTP_BRP 1#define CONF_CAN0_BTP_TSEG1 71#define CONF_CAN0_BTP_TSEG2 24#define CONF_CAN0_BTP_SJW 20#define CONF_CAN0_DBTP_TDC 0#define CONF_CAN0_DBTP_DBRP 1#define CONF_CAN0_DBTP_DTSEG1 17#define CONF_CAN0_DBTP_DTSEG2 6#define CONF_CAN0_DBTP_DSJW 3

author: ataradov
author_rank: Super Contributor
timestamp: August 02, 2022, 10:47:59 pm
content: The settings look fine, but the baudrate indeed looks off. Zoom in on a bit and measure it carefully. Don't just assume the number because it is close enough.What is the source of the 16 MHz clock?

author: matti.salo
author_rank: Contributor
timestamp: August 03, 2022, 03:39:57 pm
content: Mckl is running at 48MHz and i have gclk with divider 3 providing the 16MHz clock source for CAN. As far as i have understood that the internal 48MHz should be accurate enough? I also have 32khz external crystal, but i don't know if i need it anywhere.I checked the clock configuration and also the other board. Now the can communication started to work, but i'm unsure in the end what i had wrong.

author: ataradov
author_rank: Super Contributor
timestamp: August 03, 2022, 03:59:48 pm
content: Internal 48 MHz is a simple RC oscillator that is not accurate enough for this.You need to use DPLL with 32 kHz oscillator as a reference.

author: matti.salo
author_rank: Contributor
timestamp: August 04, 2022, 06:43:30 am
content: Thanks for the advice. These clock configs are quite new to me. I've now configured the FDPLL and it shows me 47.999MHz. Should i use that clock as source for everything and just disable the internal oscillator OSC48M? What would be the best practice for this?

author: ataradov
author_rank: Super Contributor
timestamp: August 04, 2022, 06:48:04 am
content: You don't need to disable the internal one unless you have a very tight power budget. But do use DPLL for everything, especially stuff that needs stability over time and temperature.

author: Rasmo
author_rank: Contributor
timestamp: August 06, 2022, 03:31:53 am
content: Quote from: ataradov on June 18, 2019, 04:58:51 pmHi ataradov,I am trying to implement a CAN bootloader and came across your code example. I was wondering if you have any details on the protocol / format of the messages being sent to the C21, or even better an example programming application! I think I have worked out most of it although there were still a few little things that I was a little unsure of.What are these lines checking in the bootloader request check? Is this something written to memory prior to a software reset to launch the bootloader?Code:if (BL_REQUEST == ram && BL_REQUEST == ram &&BL_REQUEST == ram && BL_REQUEST == ram){ram = 0;return true;}Thanks again for your example.

author: ataradov
author_rank: Super Contributor
timestamp: August 06, 2022, 03:42:44 am
content: Quote from: Rasmo on August 06, 2022, 03:31:53 amThe document is attached.Quote from: Rasmo on August 06, 2022, 03:31:53 amThis is a software bootloader request. Application may put those values into the first 4 locations in SRAM and reset the MCU. Bootloader would check them and stay in the bootloader mode. This is also described in the document.

author: Rasmo
author_rank: Contributor
timestamp: August 06, 2022, 03:51:47 am
content: Quote from: ataradov on August 06, 2022, 03:42:44 amThank you, that is exactly the kind of thing I was looking for!

author: Rasmo
author_rank: Contributor
timestamp: August 06, 2022, 04:55:35 am
content: One more quick question. In sys_init() I can't see bits NVMCTRL or DSU in the APBCMASK register.Code:MCLK->APBCMASK.reg |= MCLK_AHBMASK_NVMCTRL | MCLK_AHBMASK_DSU;If I understand correctly this is actually enabling SERCOM4 & SERCOM2, is this intended or was it supposed to be AHBMASK instead of APBCMASK? It looks like the NVMCTRL & DSU bits in AHBMASK are default to set on reset anyway though so it probably isn't needed?

author: ataradov
author_rank: Super Contributor
timestamp: August 06, 2022, 05:01:12 am
content: This is a mistake. I fixed it later, but the published code has it. It needs to be MCLK->AHBMASK.reg = ... and the same in the sys_deinit()AHBMASK actually has those things enabled by default, so it does not really matter. It was put there for clarity and because I had a lot of size to spare for a 2K bootloader.

author: Rasmo
author_rank: Contributor
timestamp: August 07, 2022, 01:02:56 am
content: Thanks ataradov,Is the project that you uploaded tested? I have put this onto a board and I haven't had any luck getting it to work. I have moved the can_init() and added a send_response() method to try and send a message prior to running the application to test (see code below). However I don't get any activity on the CAN bus. I have started running through the code but I can't seem to get debug working with it properly to breakpoint. I can see that the registers are being set but I think the code is being optimised in a way that I can't breakpoint. I know the hardware I am using is good as I can put my original program on and that has CAN comms fine.Thanks again.Code:int main(void){sys_init();can_init();send_response(BL_RESP_INVALID);if (!bl_request())run_application();while (1){can_task();}return 0;}

author: ataradov
author_rank: Super Contributor
timestamp: August 07, 2022, 01:25:43 am
content: Of course it was tested, I did not just type it out of the blue.Breakpoints will not work properly because the project loads itself into the SRAM to enable bootloader update, and Cortex-M0+ does not support breakpoints in SRAM.I think all you need to do to make it run from flash is move this sectionCode:*(.ram_vectors)*(.text*)*(.rodata)*(.rodata.*)from .data to .text in the linker script.There are two CAN controllers on this device., Are you sure you are using correct pins?

author: Rasmo
author_rank: Contributor
timestamp: August 07, 2022, 01:58:12 am
content: Quote from: ataradov on August 07, 2022, 01:25:43 amAhh right, that would explain why.Quote from: ataradov on August 07, 2022, 01:25:43 amYes, I am using PA25 / PA24, which it looks like is the same as your project.I have just tried it on my Xplained board and it does work as expected. This however has a C21J18A, whereas the original board has a C21J15A. Is there something I need to change beyond selecting the different device?I do also have a 16MHz crystal on the original board that I am using with the original program that works but that shouldn't effect the internal clock should it? I will continue to see if I can work out what is different.

author: ataradov
author_rank: Super Contributor
timestamp: August 07, 2022, 02:00:39 am
content: Yes, you need to edit the SRAM size in the linker script. The stack is placed at the end of SRAM and right now it is in the non-existent memory.Although I just looked and it is already set for 4K of SRAM, so it should be fine.Are you checking that there is no communication with a scope or with another device? It is possible there is some baudrate mismatch, so I'd use a scope or a logic analyzer.My code uses internal 48 MHz RC oscillator. This is not ideal for interoperability, but should be good enough to see something being sent.And about selecting a different device - Atmel Studio used to add its own linker script and startup files when you do that. Check that this did not happen. I usually change the device by editing the project file in a text editor, this way AS trying to be smart does not get in a way.

author: Rasmo
author_rank: Contributor
timestamp: August 07, 2022, 03:42:26 am
content: I have figured it out. It was my bad so sorry for mucking you around a bit. I have the STBY pin of the CAN transceiver connected to PA19, which I had forgotten about, and then it is also the same as what you were using for the bootloader detect as luck would have it, which meant it wasn't doing what I thought also! All running now though thank you. Now I will just work on sorting the PC side of the transmission.In terms of setting up an existing project for use with a bootloader, do you have any recommendations for documentation on the best / common practices? There seems to be a lot out there and before I start sifting through it all I thought I would see if you had some pointers on where to look. I think I will need to add the .text memory location in the linker memory settings with ".text=0x800", is this all? It would also be nice if there was a way to link the bootloader into the application project so it can all be downloaded at once. The same with debugging the application, can I still do that with the bootloader present or do I need to have a different configuration for that?

author: ataradov
author_rank: Super Contributor
timestamp: August 07, 2022, 03:49:01 am
content: All you need to do to make existing project work with a bootloader is correct the offset int the linker script. Normally your linker script will begin something like this:Code:MEMORY{flash (rx) : ORIGIN = 0x00000000, LENGTH = 16Kram (rwx) : ORIGIN = 0x20000000, LENGTH = 4K}Change it to:Code:MEMORY{flash (rx) : ORIGIN = 0x00000800, LENGTH = 14Kram (rwx) : ORIGIN = 0x20000000, LENGTH = 4K}I would strongly advice against trying to add the code from the bootloader to the main project. But it is fine to just include a pre-built binary file. You can do it though the linker as well.For debugging you generally want to have a separate build where application is linked at the beginning. I sometimes include a "dummy" bootloader right in the project. This dummy bootloader would just jump to the application. This way application is linked at the same address, but when you prepare the final image, you need to replace the first 2K instead of just merging two images.Generally it does not matter which way you go, it is all about the same.

author: Rasmo
author_rank: Contributor
timestamp: August 07, 2022, 04:33:16 am
content: I think I am just using the default linker scripts in Atmel Studio, do you know where these are kept? I can't find any ld files in the project directory. I assume it is the file called -Tsamc21j15a_flash.ld?

author: ataradov
author_rank: Super Contributor
timestamp: August 07, 2022, 04:38:10 am
content: It all depends on how the project was created. I guess it uses the file form a device pack if you can't find it in the project. This is pretty horrible, since your project is not self sufficient.Yes, the file should be named samc21j15a_flash.ld.There is also a way to specify the offset of the .text section without changing the linker script, but I personally would copy that linker script to the project and modify it. The linker script is as much part of the source code as any other C files.

author: Rasmo
author_rank: Contributor
timestamp: August 16, 2022, 09:58:03 am
content: I have managed to make a program that downloads my binary to the micro, however I am having a few issues with verifying the data.Firstly, I found that the CRC generated by the DSU was the inverse of the standard CRC32 implementation which caught me up for a little bit. I also noticed that the size and address written to the DSU needed to be converted to words rather than bytes (although I think this was actually okay because the ADDR & LENGTH registers were offset by 2 bits anyway). However, the DSU calculated CRC differed from my CRC. I read the flash using my ICE and it was identical to the uploaded binary so there wasn't any corruption. Do you have any ideas on where to start looking? I have attached my new crc_calc code below.Code:static uint32_t crc32_calc(uint32_t addr, uint32_t size){DSU->ADDR.reg = DSU_ADDR_ADDR(WORDS(addr));DSU->LENGTH.reg = DSU_LENGTH_LENGTH(WORDS(size));DSU->DATA.reg = 0xffffffff;DSU->STATUSA.reg = DSU_STATUSA_MASK;DSU->CTRL.reg = DSU_CTRL_CRC;while (0 == DSU->STATUSA.bit.DONE);if (DSU->STATUSA.bit.BERR)return 0xffffffff;return DSU->DATA.reg ^ 0xFFFFFFFF;}

author: eutectique
author_rank: Frequent Contributor
timestamp: August 16, 2022, 10:45:58 am
content: Quote from: Rasmo on August 16, 2022, 09:58:03 amYou should be more specific about "standard" CRC-32. There are several implementations, most of them are standard:https://crccalc.com/?crc=123456789&method=crc32&datatype=ascii&outtype=0In-depth view:https://github.com/Michaelangel007/crc32

author: eutectique
author_rank: Frequent Contributor
timestamp: August 16, 2022, 01:21:29 pm
content: Quote from: Rasmo on August 16, 2022, 09:58:03 amWould you please elaborate on that? Do you mean that length of 0x100 bytes should be passed as 0x40? Then you are wrong. I have no idea how you define the macroWORDS().Section 13.11.3.1 Starting CRC32 Calculation says:QuoteIt's not the number of words you need to write into the registers. It's the number of bytes, but aligned properly.

author: ataradov
author_rank: Super Contributor
timestamp: August 16, 2022, 03:18:07 pm
content: Address and length are designed to accept direct values, but those values must be aligned at 4 bytes. With your modifications you are just doing more math for no real reason. My code is functional and tested. This CRC code was tested 100 times over in other projects.Yes, the CRC is for the reversed polynomial. There is no real single standard. This CRC implementation matches IEEE 802.3.Here is a matching C implementation:Code:// IEEE CRC32 with a polynomial of 0xedb88320 (0x04c11db7 in reverse)//-----------------------------------------------------------------------------static uint32_t crc32_tab};//-----------------------------------------------------------------------------static void crc32_tab_gen(void){for (int i = 0; i < 256; i++){uint32_t value = i;for (int j = 0; j < 8; j++){if (value & 1)value = (value >> 1) ^ 0xedb88320ul;elsevalue = value >> 1;}crc32_tab = value;}}//-----------------------------------------------------------------------------static uint32_t crc32(uint8_t *data, int size){uint32_t crc = 0xffffffff;for (int i = 0; i < size; i++)crc = crc32_tab[(crc ^ data) & 0xff] ^ (crc >> 8);return crc;}You can generate the table once and just have it in the code. Or call crc32_tab_gen() in the beginning and let it initialize the table.

author: Rasmo
author_rank: Contributor
timestamp: August 17, 2022, 09:00:54 am
content: Quote from: ataradov on August 16, 2022, 03:18:07 pmSorry, I am not saying that your code is wrong, I am just trying to learn and understand why / how you have done things.I was looking at the datasheet and it said that the LENGTH register is the 'length in words needed for memory operations.' although I see that it is left shifted two bits so you can write directly the bytes. I guess that the only time this would be an issue is if you needed to write to the AMOD register? But then I guess you could always just do that after anyway. I will revert those changes.The CRC implementation that I was using in the uploading application and a hex editor to verify, was the non reversed polynomial 0x04C11DB7 so I guess that is why I needed to do the XOR on the result.I think I have also found why I was getting the CRC error when uploading. I see that the bootloader is expecting an exact multiple of pages in the data transfer. My binary was 0x6528 bytes long so it was dropping the last 40 bytes. Would you normally pack the last page with 0xFF's to make up the 64 bytes in the sending application, rather than within the bootloader? I guess that saves some processing in the bootloader?

author: ataradov
author_rank: Super Contributor
timestamp: August 17, 2022, 03:36:41 pm
content: Even for AMOD you still can do something like this "DSU->ADDR.reg = addr | DSU_ADDR_AMOD_xxx;".XOR with 0xffffffff is a separate from the reversal. CRC algorithms generally have independent parameters bit reversal and the need to invert the result are completely independent.Often you inverse to get the final value, and if you are updating the value, intermediate results are not inversed.Yes, you need to pad the data. The write is triggered when PAGE_SIZE bytes are received, so you were not writing the last page with incomplete data. It is easy enough to fix, but I personally believe that the host must provide the padding. You don't want the bootloader to "guess" the stuff for you.I never use inversed bersion, since I see no point in doing additional work.