CAN Testing

The idea is to have 1:1 between connectors and D-SUB9, but for testing I just connected all three on the same cable with a D-SUB9 for Laptop connection. This is a stanadard 10x flat cable. Pin 10 is shield if needed. I have not connected 5V and GND on D-Sub9, but I will in the next version. Some people are fanatic about having Gnd because they do not have proper galvanic isolation. But, also 5V is usable in case you have a CAN device that needs to be powered through this cable. The 5V PSU supply 2A and the main board uses ca 200mA, so we have plenty to share within reason.

Starting testing I only switch on 120Ohm on one port at top and I start with a decent low bitrate – lets get basic CAN working before we start testing higher speeds and FD. These CAN FD ports should be capable of 12Mbps, but lets test that before we start braging πŸ™‚

The next piccture is an early mock-up of a possible extra CAN port. You connect this to module1,2 or 3 and you get an extra port. The box have room for these on the other side so the board will be upside down. The height is to fit into the box that will easely fit 3 of these. I already have M12 modules that can be tested, but to develop with that mistake of a SWD I used is hard. Also on the module below is a new, smaller SPI Flash – I am getting braver and starting to use components with connection on the sides – actually found them very easy to hand-solder to my surprice. The MCU in this case is STM32H503CB due to it’s low cost and support of CAN.

For 3 extra CAN it would be better with a scaled down version of the main board with connecting Link to Module. I was thinking of just not mounting the PSU, USB, SD-Card and Ethernet and use the same board, but it might be that a smaller board designet to stand upside down is better – lets see. One argument is that I hesitate starting on a 2nd MCU right now.

Needless to say – this module board can add anything we want – extra CAN port is only an example. Digital IO, Serial ports, analogue IO etc – you name it.

 

3xCAN – almost there

Got more parts today so I finally have connectors, USB and SD-Card mounted – lack Ethernet yet + discovered that the PSRAM has a more narrow footprint than I used. Not sure I am bothered with mounting PSRAM on this version because strapping another nCS will be difficult – one nice thing with narrow footprint is that PSRAM and FRAM will share the same size. SPI Memory use a JEDEC signature allowing you to detect what is mounted. I will have the LAN in a few days and by then have a complete board for testing/developing.

USB is working and so is power through USB meaning I can just power the board through my laptop. I must admit that soldering have been much faster than I expected, but I am serious on getting the PnP running next.

I also got the boxes I want to use and realize it will be tight with two boards and those connectors – I might need to use right-angle versions to get space and move the connectors a bit to left – we will see.

Dictionary

As you build a modular system you will implement a lot of variables in each module that you might want to change as part of configuration or see as they are sensor values. To do this I create a separate module called a β€œDictionary” that is illustrated below.

The dictionary itself is a large Index table over variables and data you want to interface with a set of standard functionality. The primary interface is CLI over USB or Ehernet – CLI (Command Line Interface) is a text based interface allowing a serial console or TCP client to be your actual interface. Through CLI you can list, set or read all variables in the Dictionary.

Most variables are real-time and can be read/write at you discression.

A protocol like Modbus or CANopen can have their own map on top of the dictionary.

Some dictionary values can be tagged as β€œpersistant” meaning they will be stored on SPI Flash then changed and read back as the system start. This includes Bitrate setting, baudrate settings, switching tables, IP/MAC settings etc – config we want the system to remember.

Data Integrity is also supported in the dictionary. Some protocols like CANopen and Modbus have the capbability to write single variables leaving an Entity (a collection of variables) with broken integrity – so we can protect those and ensure that the module only receive a complete entity.

Sensor arrays/filters are another feature. Some sensors sample faster than we are interested in so we offer techniques to store windows and filter values here rather than forcing every sensor to implement it separately.

And last, but not least – user modules use Dictionary as it’s interface according to a scheme you decide.

The nice thing about the dictionary is that the modules don’t know about it – it is the dictionary who need to know about modules – and to avoid lengthly index searching we can allow IO messages to be mapped, meanuing you do not search the index at all. At module start you can request direct access and store addresses – you can then write/read directly or through safe Dictionary functions.

CLI however will need to search the index always.

RTOS, CLI, Switch and Dictionary is a powerfully start for any embedded application. They do howether come at the price of a minimum of Flash and SRAM.

C++ CAN Interface

I am waiting on parts for the USB, SD-Card, Ethernet and Modules so the next task I can work on is the CAN interface. The actual barebone CAN interface is autogenerated by STM32CubeIDE in C, so all we need to do is to create a proper IO queue, deal with timing and error handling. Most of this is easiest done on HAL layer in C. The one you need to watch out for is error handling because ST’s CAN interface have error counters that will stop the interface unless you process that correctly and you might be running for a long time before reaching 255 Errors on send or receive.

And sorry to say – setting up CAN is a fiddle if you have never done it before – if I am lucky it work within a day, but I would estimate a weeks work to be sure knowing that I sometimes have used 2-3 weeks in the past.

Galvanic Isolation

As mentioned before all ports except USB are fully galvanic isolated . 24V, Ethernet and all CAN ports. The isolation level on CAN is actually 1500V for 1 minute. My weakest point here is the SSR used for 120Ohm, but I will look into that. The CAN Tranceiver itself handles 5000++V for 1 minute. What you need to know about galvanic isolation is that it will protect you ports against spikes and mistakes, but it will not protect them forever. I older equipment or very low cost equipment they still uses diodes and chokes which also protect to some level, but not even close to what a galvanic isolation will do. Also – with full galvanic isolation on CAN we can let CAN be a twisted pair and ignore ground. But, I have output Ground and Shield for those who insist (or need) on it. Ground was mostly needed because of diodes that ancored the H and L lines to 12V etc. Galvanic Isolation cost between 5-10USD rather than 1 USD, but these are the bucks you don’t want to save.

Noise

Galvanic isolation do not remove noise, so if you run wires close to a noisy motor etc you might need to shield the cable. You will detect noise as CRC errors on incoming messages. I accept to loose about 1 message per hour, but you need to evaluate your own cabling and situation.

Timing

To set up CAN you also need to set a few timers that decide then the CAN interface read bits. These decide your bitrate and you need to use a standard that is very close to what the rest of the CAN network uses to achieve arbitration. The recomendation that many uses is CANopen so I will stick to that. The tricky part here is that every MCU have their own clock system and you have a clock speed set by Clock Configuration in STM32CubeIDE in my case and a prescaler on each CAN interface – and to complicate things these are FD-CAN ports capable of 12Mbps speed. At precent I want to stick to β€œclassic CAN” capable of 1Mbs. I notice that my maximum default speed setting is almost 46Mbps which will not work so we will need to configure the following correctly:

CAN Clock Speed. On some older MCU’s I needed to adjust the MCU speed itself for this to use a speed/precaler combination that generated a similar speed. I have not done this on H723 yet. I notice that my current CAN Clock Speed is 137.5Mhz (Maximum) and that H723 can adjust this separately from main clock speed.

Nominal Prescaler will set your bitrate by dividing the CAN Clock Speed and keep in mind that this needs to give a bitrate that is the same for the entire CAN network which will consist of devices with complete different clock settings. I notice that to achieve 1Mbps I will need a Prescaler between 45 and 46 – so I might need to look into CAN Clock Speed to get this accurate. Keep in mind that the CAN receiver most likely will work as long as you set a bitrate it can receive since the silicon is most likely to check CAN frames and ignore speed on receive – so if you have a 1:1 bus you can actually set one device to 1Mbps and the second to 500Kbps and it will β€œkind of” work as long as you don’t need arbitration – this is an actual error I corrected on a mobile vehicle for 1,5 years ago that took me by surprice.

Nominal Time Quantum is calculated for you.

Nominal Time Seq1 and Nominal Time Seq2 are the timers that is a bit tricky as they should follow CANopen standard.

Nominal Time for one Bit is calculated and should be 1ms for 1Mbps.

Nominal Baud Rate should be 1000000 bit/s for 1Mbps.

So to summarize on Timing – you will need to fiddle with CAN Clock Speed and Prescaler to get correct bitrate and once that is done set Seq1 and Seq2 correct. I have done this before, but every MCU is so different that I sit and fiddle every time – my tools are searching the internet and asking OpenAI + testing. And you have to do this for every bitrate you support. But, keep it simple (1) set bitrate through clock speed and prescaler and (2) set timers.

Testing

In my case I will create a CAN Network with 4 CAN Nodes (1) My Laptop through some standard CAN/USB Adapter – I hav access to Peak and IXXAT so I can use those as reference. The three others are the three CAN ports on the board. This allows me to do some clever arbitration tests as I can insert messages simultaneously and see whom comes out first.

Termination

I have added a 120Ohm termination resistor that is SW configurable on every port so I switch this on for every endpoint.

Barebone Queue

CAN interfaces always have a small queue for sending/receiving in Hardware that we need to service. I always read/write those with longer SW queues inside – that said CAN is usually so fast even on slow bitrates that it is seldom I use much queues. It is mostly if you want to support hard bursting. Bursting is the capability for one device to send continiously and expect that the receiver will cope – so we add a in/out SW queue on 100 or 1000 entries as per need. The only tricky part with this queue is that one end will be in HAL C Code and the other end in C++ code, so we use a C FIFO module as base. And this 2-way queue needs to be message orriented with age timers added.

Age timers are actually nonsence since we in most cases will send/process messages within 50ys, but they are there for the rare case that we don’t. We have control over receiving, but we might be delayed on sending due to arbitration – and we need error handling for arbitration errors (messages that do not send due to low priority on a busy system).

The CAN IO Queue should be in ISO 11892 format – I know what that is for CAN 2.0 A/B, but CAN-FD is new for me, so I need to check if it is more than extending from 8 byte to 64 byte payload.

The β€œother end” of a CAN queue is the switch which in reality will forward to host β€œas is” or to a protocol like CANopen, J1939 or custom depending on how you use this board.

Error handling

Auto Retransmission allows the CAN HW to re-transmit a few times before it gives up. We still need to handle the case where it gives up which most times are arbitration error. But we also need to remove messages that never send to avoid that our out queue overflows.

Send/Receive error counters will trigger callback if they reach thresholds.

And it might be some that I have forgotten – On most HAL interfaces I just use the HAL, but on CAN I read the registers looking for error settings due to past experiences with weird errors that are raised. You could say I have trust issues with ST HAL and CAN πŸ™‚

C++ Class Model

The diagram below is a simplified class model.

Barebone CAN

CAN is one of the exceptions where I read the barebone interface and check if I need to do something. HAL is usually good, but I have experienced issues with lack of proper error handling here in the past- usually I end up adding a few bits to HAL.

HAL CAN 

HAL implement an interface and I prefer to us this β€œas is”.

alCAN

I add my own CAN low level class that pick up barebone access, register settings and added HAL work. The interface to this is C++ SW queues and a seperate Send & Receive task called by RTOS.

RemoteIOCAN, CANopen, J1939 are just examples of the next level. Actuall the switch that is not shown here will receive messages from alCAN and forward them as requested.

One important note is message hand-shaking. Due to the nature of the RTOS I use that is most likely instant. Assume you receive a SDO Read request you will receive the message, process it and send the responce within 200ys or something.

Thanks for reading the mega rant πŸ™‚

Easy SPI C++ Interface

I am using W25Q16JV as SPI Flash and for this purpose I need a generic SPI driver that I can use in a W25Q16JV Driver. This is part of my Abstraction Layer in C++ where I add a little extra code to avoid having to deal with HAL/HW Interface on every SPI device I use. In fact, I prefer to think of SPI as a duplex serial interface.

Speed

W25Q16JV datasheet state a speed of 133Mhz, but as I tested max speed with H723 it failed. I set SPI clock to 250Mhz and SPI to 125Mhz which should have worked I thought – but as OpenAI pointed out the max speed using single SPI is 50Mhz and you can β€œkind-of” read that in the datasheet. I managed to get the SPI working at 31.25Mhz and tested it closer to 40Mhz, I actually set it to 50Mhz, but that did not work – so at the end I left it at 31.25Mhz using 250Mhz as SPI clock and 8 as prescaler. I understand from OpenAI that this is a common problem.

I am a bit dissapointed, but to be honest 31.25Mhz is more than sufficient speed. If I need more speed I can implement Quad or Octal SPI which actually support 133Mhz on Fast Read. It is also possible to get faster spead using only Fast Read commands I understand, but I am not bothered. This was an interesting lesson learned because it is the first MCU where I actually could have used higher SPI clock speeds.

31.25Mhz is actually 3.9Mbyte per sec and we only write/read very small blocks so speed is sufficient for reading/saving config or storing new firmware images. I am still waiting on PSRAM which also has a stated speed of 133Mhz, so will try that later. The only timing we need to be aware of is then we erase the chip or larger blocks – full chip erase is probably ca 0.5 sec – I will test later.

Capacity

16Mbits is 2Mbyte of extra Flash data. I was considering a larger SPI, but to be honest I am not sure what to use what I have for and I don’t need a lot of Flash I don’t use. In fact for config I could have borrowed a few K of internal Flash, but I have in the back of my mind that I might need SPI Flash for bootloader as I have not investigated the details of the H723 bootloader yet.

Read JEDEC ID

JEDEC is a standard established in 2003 that allows you to send code 9F and get a two byte standard telling you memory type ID and capacity ID back. This means you can use one of many SPI devices with the same footprint and depend on Firmware to detect what is mounted. Winbound have several other functions as well for the same purpose, but JEDEC will work on non-Winbound as well (I have tried).

Class Model

β€œal” stands for β€œAbstraction Layer” and I use that prefix on modules where I expect names to conflict with HAL/Driver names. I removed the β€œH” from HAL because AL can be Hardware and/or Software abstraction. The UML model above illustrates the 4 layers we have:

Barebone SPI means SPI interface in assembly or very low, level C where you mingle the registers yourself. I will NEVER recommend that unless you are desperate for space or speed. Back in the old days I wrote a lot of assembly myself, but these days you will need to argue hard to add assembly.

HAL – Hardware Abstraction Layer type of drivers are provided in C for most MCU’s these days. I always use these unless I can make a decent argument why I should not.

alSPI – This is my own Abstraction Layer in C++ and this is 99% generic. I preffer to use HAL and tools provided by the vendor and then put stuff like alSPI on top of that – the cost is that I have to accept 1% of code to be vendor specific because I need til β€œwire” alSPI to pins used by HAL etc. But, it is ONLY the init/wiring that is vendor specific, meaning code that uses alSPI is safe.

So – does alSPI use bit-banging, interrupts or DMA ? It uses HAL and I don’t care unless I need to care if it becomes to slow. And on a SPI Flash it is only the time used in Erase Chip you need to have control over.

Also keep in midn that a C++ class can be bare bone. If I need to go barebone I would do so inside a C++ class/function.

WQ25JV16 is the Winbound SPI Flash driver in C++.

On 3xCAN I have added lwIP (Ethernet), RTOS, HAL and Flash-SPI and am currently using 139Kbyte FLASH with 373Kbyte free. lwIP answer for a lot, but I also need to add CANopen, J1939 and ProfiNET stacks later – I estimate total application might pass 200Kbyte on Flash. Application uses 48Kb SRAM + 32Kb (lwIP) – I still have 581Kbyte SRAM free. CPU wise I will be mostly in idle mode – I have considered a smaller MCU for this board, but the 3rd CAN interface is worth a lot + once you start using a MCU you also start buying bulk that bring it down in cost – not to mention the hours you spend making software for that MCU.

C++ Performance Trick

C++ code is often faster than C code on API layers because of virtual functions and how things work in C++! If you have layer upon layer in C you need to have a function that call a function on the next layer and the same cascade with callbacks. In C++ you implement Send in alSPI and as you call Send() on W25Q16JV it actually calls alSPI::Send directly. The same goes for events coming back through virtual functions. So, C++ can allow itself a much more abstracted API without paying much performance penalty . just know what youre doing!

Myself I use what I call β€œEmbedded C++”. This is C code + a subset of C++. Modern C++ also cosist of a lot of features I would never use! In embedded development you seriously need to know what you are doing and why!

3xCAN – Switch

Below is a high level draft of switching logic that I want to implement on the 3 x CAN board. The β€œstandard” firmware on any CAN Hub is that you forward CAN messages to/from USB or Ethernet, but we can do so much more than that by creating programmable switch logic.

I have called this β€œ3 x CAN”, but as mentioned before it also have 4 x modules/links to extend functionality. The most obvious option is that we connect Link to Module1 on a different board and get a total of 6 x CAN port’s. CAN messages are standard on layer 2, we have a 11 or 29 bit ID + a 8 byte to 64 byte payload. So we switch on ID ranges + we need to implement something similar for Ethernet, USB and serial links. As mentioned the default, initial config is low latency remote IO.

The yellow arrows are to remind me about the number of FIFO queues I will need – more exactly 20 – I plan something like 5K on each FIFO, meaning we use ca 100Kb on Queues. I have ca 400Kb unused so I have plenty of SRAM including 32Kb for Ethernet.

The job of the switch is to read input messages and put them into an out queue with as low latency as possible. Due to the RTOS I am using I expect < 200uS (0.2ms) latency inside the Hub. Actual latency will vary with load obviously.

CLI – Command Line Interface means I can use tools like Putty to write read text commands as a low cost config tool. Simple text with a line-feed.

Link and Module are full duplex serial ports capable of at least 16Mbps speed – combine this with a low latency transport system we should have a pretty awesome Hub.

3 x CAN – Fixing a Crystal Error

I can’t fix the crap above – working with STM32CubeIDE you get used to SW bugs and slow perfomance as can be expected by most Java applications – that screen is something you see a lot…

I got the MCU running with no crystal, added a 25Mhz crystal and it worked. Added some other components and detected that my crystal was very unstable on/off.

HSI worked fine, but crystal was moody.

Double checked all MCU pins, replaced crystal, measured noise on DC/DC before I finally replaced the 18pF capacitors associated with the crystal and voila.

1/2 day wasted on a bad soldering point…

These bugs are a bit hard as you bring a new board to life and don’t have the confidence that this should work. I have used this crystam before, but I had a new DC/DC next to it + it is always the possibility of a dodgy MCU…

3 x CAN Board coming up

It is a hard, tedious job to solder a board with 140 components manually, but it is only two components on this that is hard to solder – one is the MCU  or more correctly – I have chenged my soldering settings and this was straight up, no fuzz. I used higher temperature and it was straight on. I am a bit curious about that spider in the left, bottom corner thought. So much easier with my old SWD connector. So far only one minor bug. Will be exiting to see if I get Ethernet up running in a day or two.

PnP Control System

I bought a Monster8 board some time ago because I needed a control system with 5-7 RAMP’s for my PnP machine. I got a bit surpriced that no-one else uses this for OpenPnP, so I might be up for a surprice. But, lets get cracking. This board is very powerfully and much cheaper than the alternatives. The only challenge is that it lack a few 6-12V PWM ports, so I added a very simple 4 x PWM board as well (not pictured).

I have a draft of the control system below:

My design is based on LumenPnP which is a variant of OpenPnP, but I have done so many adaptions that it is not much left of the original design anymore. I actually build the frame some 1,5 year ago and was a bit annoyed with some of the crappy mechanic solutions on the original. The new design they use is improved and actually implement some of the same modifications I already had done.

#1 Stronger Frame. I decided to add an extra 2020 frame to get the machine more stable.

#2 I added tank belts for wires and pneumatic.

#3 I moved all moving parts away from the working plate to achive less vibrations. Pumps and solenoids makes the machine vibrate.

#4 I designed a new head that easily can be expanded to a 4 or 8 head machine later. That said I will start with a modified version of the LumenPnP double-head (illusrated below) as a start because adding heads also need more RAMPS, Sensors and Pneumatics. I modified the head to match sensors and belts I was using.

The design abobe is different from the original design that had some crappy screws that needed adjusting below – something that is a problem since the working plate is straigh below.

#5 More End Sensors. I added end sensors everythere. And one extra on Y axis as well as a 2nd RAMP here. It is two steppers working together and over time they can get out of sync so it is an easy fix to have two RAMPS and two sensors to enable adjusting the frame.

#6 Linear bearings. The original design used rubber wheels, while the new design uses linear bearings as illustrated below. I will add the linear bearings, but I will not use my own head design yet – I will add the new heads as we move to a machine with more heads later.

 

#7 Standard End Sensors. I ignored the special LumenPnP sensors and designed for standard sensors available on AliExpress.

#8 Standard Control System. I ignored the special LumenPnP control system and decided on Monster8 as base. To be honest I also have two other 3D printer systems based on AVR, but I don’t want to work on AVR anymore and Monster8 uses STM32F407Rx.

To complete the machine I plan on using Raspberry PI with a screen and keyboard to run OpenPnP connected to Monster8 through USB-C.

I have lacked tim/motivation to start on this, but my latest CAN board is some 140 components and I really feel the main of not having a PnP machine.

I will have a dig into Marlin, Klipper and Smoothieware for a possible port – but, I have not decided on that yet.