SAMC MCAN CANbus controller message RAM management - Page 1
Forum section: electronics / general

author: Simon
author_rank: Unknown
timestamp: September 28, 2023, 09:15:31 am
content: I'm starting to write code for the CAN bus controller on the SAMC, this is a bosch MCAN controller used under licence.I have one message from slot 0 transmitting but other slots are a challenge. I have tried to check that my memory address math is correct and that I am pointing to the right location in RAM memory. I have noticed that the debugger mentions other variables or even functions when I look at the location.Is it that if I simply point to the address in the code without claiming this space as a variable that the RAM my get used by something else as the compiler is unaware of the dual use?Am I safer creating say a structure for each buffer to be mapped to in RAM in a similar way to how the registers are mapped in the chip header files so that it's clear that this space is off limits to other stuff?

author: ataradov
author_rank: Super Contributor
timestamp: September 28, 2023, 04:09:55 pm
content: Quote from: Simon on September 28, 2023, 09:15:31 amYes, of course. How would compiler know?Quote from: Simon on September 28, 2023, 09:15:31 amThis is the only correct way of doing it. Apart of reserving the memory in a linker script. But that's just extra work for no benefit.

author: Simon
author_rank: Unknown
timestamp: September 29, 2023, 07:56:42 am
content: I see, well this is the first time I work with something where I am using raw RAM like registers.

author: Rudolph Riedel
author_rank: Regular Contributor
timestamp: October 05, 2023, 08:56:22 pm
content: Here, this is what I am using for a while now:Code:#define CAN0_TX_BUFFER_NUM 4#define CAN0_RX_FIFO_NUM 32#define CAN0_ELEMENT_DATA_SIZE 64#define CAN0_STANDARD_ID_FILTER_NUM4#define CAN0_EXTENDED_ID_FILTER_NUM4struct can_tx_element{volatile CAN_TXBE_0_Type T0;volatile CAN_TXBE_1_Type T1;uint8_t data;} __attribute__((__aligned__(4)));struct can_rx_element{volatile CAN_RXBE_0_Type R0;volatile CAN_RXBE_1_Type R1;uint8_t data;} __attribute__((__aligned__(4)));struct can_rx_fifo_element{volatile CAN_RXF0E_0_Type R0;volatile CAN_RXF0E_1_Type R1;uint8_t data;} __attribute__((__aligned__(4)));struct can_standard_message_filter_element{CAN_SIDFE_0_Type S0;} __attribute__((__aligned__(4)));struct can_extended_message_filter_element{CAN_XIDFE_0_Type F0;CAN_XIDFE_1_Type F1;} __attribute__((__aligned__(4)));static struct can_tx_element can0_tx_buffer;static struct can_standard_message_filter_element can0_standard_id_filter;static struct can_rx_fifo_element can0_rx_fifo;static struct can_extended_message_filter_element can0_extended_id_filter;And then I have this in my setup function:Code:if (can_channel == CAN0){/* TX Buffer Configuration */can_channel->TXBC.reg = CAN_TXBC_TBSA((uint32_t) can0_tx_buffer) | CAN_TXBC_NDTB(CAN0_TX_BUFFER_NUM);/* RX FIFO Configuration */can_channel->RXF0C.reg = CAN_RXF0C_F0SA((uint32_t) can0_rx_fifo) | CAN_RXF0C_F0S(CAN0_RX_FIFO_NUM) | CAN_RXF0C_F0OM; // FIFO 0 overwrite mode/* ID filter buffer configuration */can_channel->SIDFC.reg = CAN_SIDFC_FLSSA((uint32_t) can0_standard_id_filter) | CAN_SIDFC_LSS(CAN0_STANDARD_ID_FILTER_NUM);/* ID filter buffer configuration */can_channel->XIDFC.reg = CAN_XIDFC_FLESA((uint32_t) can0_extended_id_filter) | CAN_XIDFC_LSE(CAN0_EXTENDED_ID_FILTER_NUM);}

author: Simon
author_rank: Unknown
timestamp: October 09, 2023, 07:49:46 am
content: Thank you. I'll take a look, I tried something similar but ended up with blank messages so I have probably made a mess of the pointers.one thing I have found is that one a transmission is triggered is just keeps going. I start it with the bit in the TXBAR register.

author: Siwastaja
author_rank: Super Contributor
timestamp: October 09, 2023, 07:53:32 am
content: I have attached a spreadsheet I made some years ago, I hope it's helpful. Just modify numbers in yellow highlighted cells. It even "generates" code you can copypaste. This reduces the risk of mistakes when calculating addresses into message ram.This was for STM32H7 but I guess the MCAN is exactly the same on SAMC? Maybe worth checking if the total amount of memory is the same.

author: Rudolph Riedel
author_rank: Regular Contributor
timestamp: October 10, 2023, 04:42:48 pm
content: Quote from: Siwastaja on October 09, 2023, 07:53:32 amNo, the SAMC21 is using the main memory while the STM32H7 have a dedicated 10kiB SRAM that two units have to share.QuoteThe ATSAMC21 come in variants with 4/8/16/32 kiB of SRAM, whatever the reason for the first two variants to even exist is as there also is the ATSAMC20 without CAN.

author: Simon
author_rank: Unknown
timestamp: October 10, 2023, 06:33:10 pm
content: Well the mcan peripheral seems to be used in many devices, I guess the RAM settings will be the same.

author: ataradov
author_rank: Super Contributor
timestamp: October 10, 2023, 06:42:04 pm
content: RAM layout and requirements are the same. The only difference is where the address registers point.

author: Simon
author_rank: Unknown
timestamp: October 26, 2023, 10:34:14 am
content: Quote from: Rudolph Riedel on October 05, 2023, 08:56:22 pmSo I am starting to look at writing my code, thank you for sharing this. I am confused with how you fill the registers. You are passing the address of the structure directly to the register? My understanding is that these registers want to know the address of the variables (structures) relative to the start of the RAM address, which I guess is kind of why only 16 bits of register are provided as the RAM does not cover the full 32 bit memory space.Thinking about it a bit more, as the RAM starts at 0x00000000 in it's own "space" and never exceeding 32kB or 15 bits, allowing the first 16 bits to be truncated leaves the RAM start relative address from the actual address which takes out the human error.So how is it guaranteed that the structures are all put within the limit of the 4352 consecutive words that the controller can address other than logically being declared one after each other they will be allocated one after the other. 4352 words is more than my 16kB RAM anyway.

author: ataradov
author_rank: Super Contributor
timestamp: October 26, 2023, 03:21:47 pm
content: Elements in the array are guaranteed to be consecutive and you providing the pointers to the arrays. The arrays themselves can be located in any way, they don't need to be consecutive.CAN itself can use any addresses within 64 KB block. For devices with higher memory size or where SRAM does not start at 0, there is an additional register that specifies the higher 16 bits of the address. All arrays still must be located within any 64 KB block in the SRAM.

author: Simon
author_rank: Unknown
timestamp: October 26, 2023, 06:08:17 pm
content: No it's any ~17kB block / 4352 words

author: ataradov
author_rank: Super Contributor
timestamp: October 26, 2023, 06:10:48 pm
content: What is " any ~17kB block / 4352 words"?

author: Simon
author_rank: Unknown
timestamp: October 26, 2023, 06:41:51 pm
content: https://www.bosch-semiconductors.com/media/ip_modules/pdf_2/m_can/mcan_users_manual_v331.pdfpage 46 section 2.4

author: ataradov
author_rank: Super Contributor
timestamp: October 26, 2023, 07:59:43 pm
content: So? It does not have to be continuous. This is just an illustration. You specify the start of each section via TBSA, F0SA, FLESA and others. All those sections can be located in any random order. And they don't need to be maximum allowed size either, since you specify the number of elements in each array. If you don't have any extended filters, you don't have to reserve the RAM for them, just specify 0 elements.All offsets are 16-bit in size, so all the buffers must be located in the same 64 KB section. If device has less than 64 KB, then this is automatically met. If device has more than 64 KB, then it depends on the way CAN IP was integrated. For example, SAM V7x have CCFG_CANx registers that contain 16 MSB of the address. And SAM E5x does not do anything, so CAN buffers must be manually placed in the low 64 KB of SRAM.

author: Siwastaja
author_rank: Super Contributor
timestamp: October 27, 2023, 05:20:27 am
content: Just use the spreadsheet I posted earlier, or make something similar on your own. Offsets can be calculated in 5 minutes on a napkin, but spreadsheet is handy as it can be iteratively modified.

author: ataradov
author_rank: Super Contributor
timestamp: October 27, 2023, 05:24:15 am
content: Why not just let compiler to calculate all that for you?

author: Simon
author_rank: Unknown
timestamp: October 27, 2023, 06:50:53 am
content: I see, well that's one thing less to worry about.

author: Simon
author_rank: Unknown
timestamp: November 30, 2023, 09:13:13 am
content: Well I do have CAN transmissions working now, took me a while to get around to testing my code between things.As to the addressing I found a document on the Bosch website that stated that the memory should be continuous but not essential.What worries me with doing it the "easy" way of letting the compiler handle where stuff is is that if I say move to the PIC32CXSG series these have up to 256kB or RAM so if the compiler were to place the variables beyond the first 64kB there would be a problem.I guess that I'd have to create a single structure that is pinned to an address so that the same letting the compiler work out the addresses works.

author: ataradov
author_rank: Super Contributor
timestamp: November 30, 2023, 03:40:40 pm
content: Yes, on parts with more than 64KB you would need to place those structures into a separate memory section and link those first.SAM V7x have a separate register that configures upper half of the address, but I guess this was dropped because it is just extra hardware for something that is trivial to solve in the software.

author: Simon
author_rank: Unknown
timestamp: November 30, 2023, 03:50:48 pm
content: so can I do somethiung like:struct canvars{fiforxfifotxfilters}all of these are arrays of other structsmemory address of fiforx= (uint32_t) can0variables.fiforxie, can the address of an array that is part way down a struct be assigned like this with the casting to extract the address?

author: ataradov
author_rank: Super Contributor
timestamp: November 30, 2023, 04:03:21 pm
content: Yes, sure. In-memory layout would be the same as if they were all separate variables.

author: Simon
author_rank: Unknown
timestamp: November 30, 2023, 04:54:01 pm
content: So I'm working on the message reception handling. The FIFO has a get index, so that is where the last unread data is at, how is this incremented? does the controller detect the data read from that location and increment it.I don't think I am supposed to do anything but I have not found any explanation as to how it behaves.

author: ataradov
author_rank: Super Contributor
timestamp: November 30, 2023, 05:29:32 pm
content: After you are done with the data, you write the index back into MCAN_RXF0A or MCAN_RXF1A and it automatically frees the FIFO entry.Note that you don't have to acknowledge each message if you are processing more than one. Just write the last index and it will discard all of the handled messages.It is described in the section "FIFO Acknowledge Handling".

author: Simon
author_rank: Unknown
timestamp: November 30, 2023, 06:25:53 pm
content: Ah right, thanks. Will give that a wirl tomorrow.

author: Simon
author_rank: Unknown
timestamp: December 01, 2023, 12:03:19 pm
content: There are two interrupt lines in the CAN controller, the Microchip datasheet carries this straight across as there being a register to enable each line out of the peripheral and a register that is used to select which interrupt line each interrupt condition fires.I always assumed that the CAN0_Handler and the CAN1_Handler ISR's are one for each of the two controllers in the microcontroller. Is this the case or are they in fact the 2 lines of each MCAN controller?There seems to be two ways of handling messages received. Set them as high priority and fire a HPM interrupt, the information about the received message will be in HPMS, the other method is to have the interrupt fire when a message goes into the FIFO and use the get / fill / set indexes which enables multiple messages in multiple buffers to be read out at a time.I'm not sure which one I should be using, either way an interrupt fires every time a message is received and so it's the same? the only reason I can see for the two mode is that the HPM allows you to react instantly to a specific message id arriving while with the RF0N interrupt one can collect up several messages at once which would be useful if say the can interrupt had to be suspended or instead of using the CAN controller generated interrupt and knowing how many messages would arrive in a given time frame allowing for a sufficient buffer size the status flag can be checked periodically and all of the messages dealt with in one go.For example, I am running a machine with two motor drivers, I need to receive data from each of them, these messages come in every 32ms and the data from both is used to make a decision every 100ms so responding to the reception of one message at a time is perhaps not worth the interrupt overhead, I may as well wait until I am ready and pick up the messages together.

author: ataradov
author_rank: Super Contributor
timestamp: December 01, 2023, 03:31:58 pm
content: M_CAN supports two interrupt outputs. In this device only one line is connected and Interrupt Line Select (ILS) must be set to 0. Two interrupt handlers are for two instances of the M_CAN controller.32 ms is a lot of time, any method should work. If you have multiple frames accumulated with them coming in every 32 ms, then something is not right with the processing.