SAMD support working

Hey guys, I’m working on supporting SAMD (like the Arduino Nano 33 IoT)

I’ve got it from this:

to this:

So that’s some success. BUT there are a number of, well, lets call them challenges…

Here is the current state of the code:

Its a mess at the moment, with hard-coded stuff mixed with debug code, mixed with the direction it’s actually supposed to go…

I’ll post a little bit about the issues below, maybe someone has some ideas…

1 Like

The SAMD has many Clocks, Timer/Counter (TC) Units and Timer/Counter for Control (TCC) units, so it is a bit complicated.

I’ve been focussing on the TCC units for now, since they support the cool stuff needed for 6-PWM with dead time insertion and interrupts for current sensing. In theory the TC units support PWM as well, so they could be used for 3-PWM…

To spice things up a bit, the SAMD can’t map any TCC Unit channel to any pin, but has fixed pin assignments. The different TCC Unit outputs are mapped to different pins on the Nano, with each pin having up to 2 TCC signals, which need to be configured via the pin configuration registers (peripheral configuration).

To make matters even more exciting, it seems that the facilities you can use to query pin capabilities on the Arduino Nano 33 IoT are badly messed up (or I have some kind of bug in my code I just can’t see!) - variant.cpp looks ok, and matches the pinout diagram, but when I use the get-Pin-Information functions I get completely different things coming back - so clearly something is overwriting the variant.cpp data, at the compile-time or at the run-time level, I don’t know.

This means however that you can’t reliably query the pin information to do the TCC setup, especially relevant in this case where each pin could have 2 different TCC outputs, and you want to find a matching TCC Unit across the 3 pins if possible…
Unless this can be resolved it means this information will have to be provided explicitly to the initialization, or it won’t work.

Another challenge is setting the frequency. At the moment it is fixed at 23.4kHz, and it has to be controlled via the Pre-Scaler and the TOP-value of the PWM, i.e. the resolution.

I see the STM code fixes the resolution at 4096 increments - I find this very high… what’s the rationale? @Antun_Skuric could you comment on this?
Won’t this be much higher than the horizontal discretization you will achieve (based on the iteration rate)?
Is it to have “space downwards”, when running at very low voltage limits?

Anyway, I don’t see any real way to set the PWM frequency to arbitrary values… Having a decent resolution already limits you to a very low pre-scaler value on the 48MHz clock of the Nano 33 IoT… Values in the 20-50kHz range can be achieved by limiting the resolution to between 500 and 1200 increments… does that seem reasonable? Does SimpleFOC need that 4096 increment = 12 bit resolution?

Hey Richard,

Very nice work, thanks a lot for taking the time to writing the code for this board. :smiley:

So for initial support i think 23kHz is a good solution. For example for Arduino boards atmega328 and 2560 I’ve fixed the frequency value to 32kHz. It can be changed but not to arbitrary value. It can be changed with prescalers. So it’s either 32kHz or 16khz or less, which are all a bit low.

Anyway you can see for example a code for esp32 where there is a short code which basically searches for the biggest prescalers which will set an arbitrary frequency with suffisant owl resolution. :grinning_face_with_smiling_eyes:

There is no need for high pwm resolution 4096 is maybe a bit too high. But if the board a are capable of doing it, it is better for control. More smoother. This can be seen much more for stepper motors since theh have much more pola pairs and need much higher resolution in measurements and pwm setting. I would not go as low as Arduino’s 255. Try to go at least to 1024 or more. That should be good.

Also there is no need to go very high frequency of pwm. From 15 to 30khz center aligned is perfect!

That is great progress!

Have you checked out Paul Goulds work in this area?

I think he may have helped David with our esp32 solution. His github has a new (last month) v3 ino with tcc0 setup.

Could you make an opinionated implementation e.g it assumes tcc0 but provides a mechanism to override e.g using build flags or weak functions?

Hi Owen, thanks for the Tip, I had not come across Paul’s driver, that’s very cool. I’ll also check out his code, thanks!

I was planning to go this way:

  • for 6-PWM it has to be TCC0 (or TCC3 on boards that have one). This is because only these units have 4 channels and can do the dead time insertion. So for 6-PWM it has to be opinionated, and would fail with error like the STM32 version if you try to use incompatible pins

  • for 3-PWM I was planning to make a flexible approach (like the STM32 implementation) where it can use any pin that has a TCC or TC peripheral attached.

I was planning to attach all TCC units to Clock 4, and always use the same PWM resolution and pre-scalers, so they’re all in sync step-wise. Then I plan to only start the clock after all units are configured and attached, meaning they should be perfectly in sync, also from the start-time. However, when using more than one BLDC this would require a small API usage change, something like a SAMDstartClock() call that would be needed after all drivers constructor function was called. Lets see if this is really doable, or just a nice idea, I’m not sure yet…

Since there are typically 2 timer peripherals per pin, and the channels and TCC units are pretty mixed up on the pins, I have code that finds common TCC units on the pins - in theory. In practice, for reasons not yet clear, my Arduino IDE and also Sloeber compiled builds give really weird results when trying to read the pin info on my Nano 33 IoT. The values completely disagree with the pinout documentation, and also the variant.cpp file… so its unclear to me where this crap comes from, I guess its a bug I’m tracking down.

But if it turns out the pin-infos cannot be reliably read, then there is no real way to do any auto-configuration… I don’t think it is a viable strategy to recreate the board info for different SAMD boards within the SimpleFOC code. So in that case it would mean you’d have to configure the SAMD driver differently, directly specifying the pin/peripheral/tcc/channel information :frowning:

I would argue that there is really no need to sync the owl signals in between motors. Except maybe for current sensing. This is something that will comme tôt je table very soon.

But i guess same chips will not have current sensing option for some time.

I also saw that there is a dma support, that is the path that we have avoided so far, bit for current sensing it will be mandatory for example in stm32 board.

So for me, all that you proposed seems reasonable. I would just like to as few things.

  • i would not change the api to sync the clocks for now
  • for 6 pwm there might be a solution where youn invert the pwm signal in software (mode PWM2 in stm32) and it add the dead time by differing the duty cycles set to each channel. The limit would be that the pairs of high+low mosfets have to be on the same timer. Like for Arduino UNO for example and for Stm32 we called it software 6 PSM mode. I don’t know, it is not a big deal but leaves a bit more freedom to the user.
  • per board hardware implementation file is not end of the world also, especially since each time only one of them will be compiled. How many of same devices are there? Are they used a lot?

Ok! I had an idea yesterday also that it could be solved with no additional APIs. But I’ll leave it for now.

I will try to do the software dead-time insertion as well. I think I get the idea, it works based on centre-mode PWM and varying the duty-cycle slightly to create the “gap”, right?
I guess I can test it using the Infineon board. I currently don’t have a 6-PWM setup, but I’ve been wanting to do that anyway for my bigger motors…

SAMD is the following boards:
image
image
plus probably a bunch of clones and variants people have made based on these.
Really a surprisingly rich world.

I became aware of it recently through the new Arduino Boards (MKR series, Nano 33 IoT). I felt it would be cool for SimpleFOC to support these new “original Arduino” boards… (after all, the GitHub is called “ArduinoFOC”)

So with so many boards, I feel the variant.cpp files need to work as expected, and managing it myself (ourselves) is not a realistic option. Really I still need to confirm where the problem is. Most probably it’s a stupid error in my own code…

I will continue on it later in the week/weekend…

Hi @runger

It’s great that you are using the SAMD. Are you using the 21 or 51 or something else. I will change over from 21 to 51 soon, as it has CAN.

The pin mapping on the SAMD is very limited when compared it to STM, ESP-32 or PSOC4. I started with the Sparkfun SAMD21 dev board, as it had all the pins accessible. I have tested the 6-PWM Centre aligned but I only used 3-PWM Centre aligned on my boards. I like the DRV8305 to handle the deadtime and shoot through protection. I also don’t use floats in my timer code as both the SAMD21 and ESP-32 are really slow at these software instructions.

I’m not using SimpleFOC purely because I had my code running before I founding it.

If you have any questions on the Timers, Comms or ADC please ask.

1 Like

Hey @Gouldpa, thanks a lot! At the moment I’m testing on a D21 (Nano 33 IoT) but I have a bunch of others in the drawer or on their way. I’ll definitely test a D51 as well.

I’m finding exactly what you say, the pin mappings are bit limited.

I’ll be doing some more work on it this weekend, and I’ll drop you a line if I get into trouble, thanks very much for the offer!

Regards,

Richard

Hey guys,

I see that there is a lot of interest in this support, and with the arm based boards the control perspective is promising. :smiley:

I am not sure if it can help you with the implementation that you are making at the moment but I’ve spent last few days writing the support for Arduino DUE. It has a SAM core, but from what I can see from the code you posted they are pretty different.
But anyway, the new code is in the dev: https://github.com/simplefoc/Arduino-FOC/tree/dev

At the moment both Teensy and Arduino DUE do not have the 6PWM support.

I am not sure what is your approach in coding this, but I can suggest you to go to the current Arduino support implementation and start from there. :smiley: This is not always the best option, but at least you start from something that works :smiley:

Let me know if I can help!

Update on SAMD: I have the configuration working in theory :slight_smile:

I spent some time on the weekend and yesterday working on it, and have code which (to me) looks ok, and also seems to work.

When I explicitly configure the TCCs, I get nice PWM signals and turning motors. If I leave it to the “auto-configuration”, then it doesn’t work…

The problem is the following:

SAMD isn’t very flexible regarding its timer outputs. Each of the MCUs pins is associated with zero, one or two timer-outputs (called WOs in SAMD-speak, and designated like TCC0/WO[2], meaning Timer 0, Output 2). Note that the different WOs are typically associated to more than one pin. Each timer has either 2 or 4 channels, but always twice as many WOs. The extra WOs can be used in certain modes (like dead-time insertion), but you can only have as many different PWM signals as the timer has channels, i.e. you can’t use all the WOs independently, only half.

To configure the PWM, we need to decide which of the associated timers to actually “attach” to the pin, since there could be up to 2 options, and the timers are not attached by default.

Ideally, all the pins for one simplefoc driver come from the same timer, but this is not always possible because of the choice of pins the user makes:

  • pins could have no timers associated
  • the pins could have only different timers associated
  • the timers could already be in use
  • the timer outputs could be “incompatible” - for example TCC0/WO[0] and TCC0/WO[4] are the same channel, and incompatible for 3-PWM

Note: all this gets even more complex if you’re trying to drive more than one motor, and unless we want major API changes we can’t do better than initialise the motors in some order, and hope that we don’t choose assignments that will make it impossible for a later motor to find a compatible setting.

So finding working combinations between 3 pins (or 6 pins) is a matter of testing different permutations of the 1 or 2 timers attached to the pins.

I’ve written the code that searches for working permutations, and this seems to work.

So what’s the problem? In order to make all this work we need a source of information about the pin to timer associations. Normally, this is provided in variant.cpp in the g_APinDescription array.
This array includes the port/PIN numbers, and the peripheral associations for peripherals E and F (which are the 2 timers that could be associated).

However, for all the boards I’ve looked at so far (MKR WiFi 1000, Adafruit Feather M0, Nano 33 IoT) the g_APinDescription array is just plain wrong. It incorrectly sets the pin attributes, and incorrectly sets the values for peripherals E and F.

The g_APinDescription disagrees with both the SAMD datasheet and (in the case of the Arduino boards) with the pinout diagrams provided for the boards.

I don’t fully understand how they can ship the software in that state, and the boards still work, but I guess the TCC units are advanced features that most people aren’t using, and the kind of auto-configuration we’re trying to do in SimpleFOC is probably not a very common use case.

So, I need a different source of truth for the g_APinDescription data.

Does anyone (@Gouldpa) have any ideas for this? I can’t so far find it in the SAMD specific headers either.

Otherwise, my approach will be to capture the information from the datasheet, indexed by PortPin since there is little variation in the timer to PortPin associations across the models. I looks like I will only have to capture 2 tables for SAMD21, I still have to check SAMD51.
Then I can use the PortPin information from g_APinDescription (which seems to be correct) to retrieve the timer associations from the chip-specific table.
I think this is better than trying to do it on a per board basis, since there are far more boards than SAMDs, and they all have the same PortPin configurations based on the chip they’re using.

Ooof, ok, sorry, here is a shorter version of my previous post:

SAMD support is almost working.

The problem currently is that the g_APinDescription data in variant.cpp, which is supposed to describe the timer peripherals, is just plain wrong, for all the boards I’ve been able to test.

So I need a source of timer-to-pin-association information if I am to make the code general and keep the same API for the driver code, i.e. allow the user to pass the Arduino pin numbers, and then initialise the timers based on that.

Does anyone know of a way to get this information for SAMD? Otherwise it looks like I’ll have to re-create this data from the SAMD datasheet.

I can confim that even for arduino due which is a SAM chip the g_APinDescription definition in variants.cpp is really reduced. For such a powerful chip with so many features they have only declared 4 pwm pins and cca 10 timer outputs. :smiley:

I guess you are right when you say that they might considered this timer/pwm implementation not to be too main stream and therefore not too important.

Have you thought about a PR against ArduinoCore-samd to fix the variants?

I suspect the variants were created (copy/pasted) by some one not working for microchip.

Hmm, I’ve just looked at PRs on that project, some are quite old which suggests maintainers aren’t super responsive

Yeah, I guess I will at least make an issue for them, and also Adafruit. I suspect it won’t be a fast way to get working support in SimpleFOC though, and that’s assuming they make the changes at all. I imagine they’ll be hesitant to change variant.cpp, error or not, since a bunch of people will be using it “as is”.

Ok, I coded up a table with the correct pin assignments, and it’s working quite well for 3-PWM. Here I connected my logic analyser to A1, A2, A3 on a Adafruit Feather M0. I set the duty cycles explicitly in this example, to 25, 50 and 75%:

image

On pins A1, A2, A3, the code finds timers TC4/WO[0], TC4/WO[1] and TCC0/WO[2], and configures them to the same frequency. Since A1 and A2 are on the same timer, their signals are aligned. Note that they are left-aligned, since TC4 is a simple timer and doesn’t do centre-mode PWM. Pin A3, as we see, operates at the same frequency, but is not aligned with the other two signals. The timers are driven by the same clock, so the signals stay in sync, but the TCC0 unit was initialised at a different time so its period is not in sync with TC4.

What you actually want is this:

image

Here we’re on pins 5, 6 and 12 of the feather board, and it finds TCC0/WO[1], TCC0/WO[2] and TCC0/WO[3] - all on the same TCC unit. So the signals are all centre-aligned and in sync.

I’ve pushed the code to my fork of ArduinoFOC (link is in a post above).

Next I will check the transitions when the duty cycles are changed, I think there are some glitches at the moment, and then test 6-PWM.

3 Likes

@Antun_Skuric - the TC units are much less powerful than the TCC units. In order to get a frequency of 24kHz to match the TCC units, I need to put them in 8-bit mode, which limits the PWM resolution to 256. I currently have it set to 250, with a prescaler of 8, which results in the same frequency of 24kHz as the TCC units.
Can we live with this?

If we want the TC units in 16bit mode, then the “NPWM” mode is limited to 730Hz or slower (!) and we would have to use their “MPWM” mode, which in turn means we could only use the TCx/WO[1] outputs (i.e. half of the TC outputs won’t work).
That’s why I went for lower resolution on the TC units, otherwise there would be a bunch of pins that could not be used.

Is that ok for you?

Anyway, I will write some docs recommending which pins to use for the boards I have.

I’ll go into more details after I finish work. You should be able to set the TOP value to 1024 (10-bit) and still get over 20kHz PWM

Hey @runger,

This is ok for me, this is the initial support and for most people it is more important that they can run the motor smoothly and have high frequency pwm than the alighment. Of course it will make a huge difference for high-power drives but for people who want to use those they will have some more time to read the docs :smiley:

For example for Arduino DUE, pwm is center aligned and timers are not. So if you mix the pins you will have the same behavior as you showed. Even worse, the timer and pwm do not have exactly the same frequency. cca 3Hz difference. They do not calculate the frequency the same way and I did not have time to dig deeper into this. But anyway, more important than all of this for me is that you can set high frequency pwm and that people can run bldcs simply. For people wanting great performance, they will probably have more time to read and to try stuff :smiley:

For me this is ok, it is not ideal but it will definitely work!

This duty cycle change has been a pain also on due, I had to make an interrupt routine which updates the pwm duty cycle when the duty cycle is finished. It is really an inefficient method, but it enables people to use pwm on any arduino uno header pin. Otherwise it would be only 6,7,8,9. So I am fine with that. :smiley:

So as you can see, I prefer leaning towards simplicity over performance and optimality, at least for initial releases :smiley: