Read ADC without reinitializing channel

The official pyboard running MicroPython.
This is the reference design and main target board for MicroPython.
You can buy one at the store.
Target audience: Users with a pyboard.
felix.letkemann
Posts: 8
Joined: Sat Mar 21, 2015 1:29 pm

Read ADC without reinitializing channel

Postby felix.letkemann » Sun Feb 19, 2017 1:34 pm

Hey there! :)
I've got some issues trying to optimize the performance of my ADC with Micropython on my PyBoard Lite. I need two ADC channels to get the voltage of two hall-effect-sensors, which is then used to calculate the angle of a motor.
Since knowledge about the angle is required to define which of the motors coils are to be switched on, it is important to calculate the angle very fast to be able to spin up the motor to high frequencies.
First I simply used .read() to get the value, but I found that to be relatively slow. So I checked out the source code of the micropython firmware and looked for the adc_read funtion. This is when I found out, that the function adc_config_channel is executed every time I do a adc.read() in my python code.
Now I am looking for a way to execute adc_read_channel without reinitializing the channel everytime I call adc.read() to save some time. To do this, I guess I would have to initialize the channel somehow and save the channel to a variable I can pass to the adc_read_channel function.
Unfortunately I could not find anything about this in the documentation. Does anyone know, if this is possible or I would have to hack the original firmware to save some time?
Or do you know if this would not save time at all? Thank you very much in advance!

Yours Sincerely,
Felix

User avatar
deshipu
Posts: 925
Joined: Thu May 28, 2015 5:54 pm

Re: Read ADC without reinitializing channel

Postby deshipu » Sun Feb 19, 2017 6:32 pm

Now I am looking for a way to execute adc_read_channel without reinitializing the channel everytime I call adc.read() to save some time. To do this, I guess I would have to initialize the channel somehow and save the channel to a variable I can pass to the adc_read_channel function.


Before you do that in MicroPython, I would recommend to test it in C and make sure that initializing the channel is really slower than all the book-keeping with the last state and checking if it changed or not. Chances are that it won't give you such a huge advantage.

felix.letkemann
Posts: 8
Joined: Sat Mar 21, 2015 1:29 pm

Re: Read ADC without reinitializing channel

Postby felix.letkemann » Tue Feb 21, 2017 7:25 pm

Why would you try that out in C before doing it in Python? The whole point of using a pyboard was to be able to use micropython instead of C, since that is a whole lot easier for me.
What do you mean by "book-keeping with the last state and checking if it changed"? The value does alwas change a little bit, doesn't it?

User avatar
Roberthh
Posts: 529
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Read ADC without reinitializing channel

Postby Roberthh » Tue Feb 21, 2017 7:41 pm

Did you determine the time needed for each of these steps:

- init channel
- read the adc value
- the net python function call
- the processing of the data got from the ADC

According to the data sheet, the sampling time goes up to about 16 µs, with faster times for small changes. An empty Micropython function call takes between 3 and 4 µs. So many short functions are worse than a longer single string of statements. Before I would change the firmware, i would first determine where most time is spent.

User avatar
pythoncoder
Posts: 1381
Joined: Fri Jul 18, 2014 8:01 am

Re: Read ADC without reinitializing channel

Postby pythoncoder » Wed Feb 22, 2017 9:28 am

An ADC read takes 94us here. When you apply a voltage to a motor winding, current increases relatively slowly (usually and to a first order proportional to the time integral of V/L). It's current which provides the motive force. Are you really sure that in this context 94us is slow?

I'd measure the current waveform before hacking code ;)
Peter Hinch

User avatar
deshipu
Posts: 925
Joined: Thu May 28, 2015 5:54 pm

Re: Read ADC without reinitializing channel

Postby deshipu » Wed Feb 22, 2017 12:25 pm

felix.letkemann wrote:Why would you try that out in C before doing it in Python? The whole point of using a pyboard was to be able to use micropython instead of C, since that is a whole lot easier for me.

But you are setting out for hacking a C program now. And it's a huge and complex C program -- the micropython interpreter. It will certainly be much less work to write a short C program that validates your theory before learning all about micropython's source code just to make that change, and then learn that it didn't improve your situation.

felix.letkemann wrote:What do you mean by "book-keeping with the last state and checking if it changed"? The value does alwas change a little bit, doesn't it?

I meant the state of the ADC peripheral, in particular its mux, not the value being read.

felix.letkemann
Posts: 8
Joined: Sat Mar 21, 2015 1:29 pm

Re: Read ADC without reinitializing channel

Postby felix.letkemann » Fri Feb 24, 2017 8:08 pm

Hey guys, thank you very much for writing such a lot of answers :)

First, I'd like to clarify what I am planning to do to avoid some confusion

An ADC read takes 94us here. When you apply a voltage to a motor winding, current increases relatively slowly (usually and to a first order proportional to the time integral of V/L). It's current which provides the motive force. Are you really sure that in this context 94us is slow?


I don't want to regulate current using the micropython, instead I am just switching the current on and of based on the position of the motor. The motor is running synchronous and does have four (not 3) coils. This is why I am building a controller myself, since all commercially available controllers are for 3 coils.
The motor does have a position encoder using two hall-effect sensors I can read out using the micropython adc converter. Using the two values, I can calculate the position using these functions:

Code: Select all

@micropython.native
def anglemapping (value: uint, in_min: uint, in_max: uint, angle_min: uint, angle_max: uint) -> uint:
    returnval = ((value - in_min) * (angle_max - angle_min) // (in_max - in_min)) + angle_min
    if returnval > 0:
      return returnval
    return 0


Code: Select all

    channel_a = int(adcA.read())
    channel_c = int(adcC.read())

    channel_b = 600-(channel_a-600)
    channel_d = 600-(channel_c-600)

    angle_last = angle
    if (channel_a > channel_d) and (channel_a > channel_c) and (channel_a > channel_d):
        angle = 360 - int(anglemapping(channel_c, channel_b, channel_a, 0, 90))
    if (channel_b > channel_a) and (channel_b > channel_c) and (channel_b > channel_d):
        angle = 360 - int(anglemapping(channel_d, channel_a, channel_b, 180, 270))
    if (channel_c > channel_d) and (channel_c > channel_a) and (channel_c > channel_b):
        angle = 360 - int(anglemapping(channel_b, channel_d, channel_c, 90, 180))
    if (channel_d > channel_a) and (channel_d > channel_c) and (channel_d > channel_b):
        angle = 360 - int(anglemapping(channel_a, channel_c, channel_d, 270, 360))


The calculation of the angle works quite nice and relatively precise. But there comes the problem: When I want the motor to speed up until it reaches 600 Hertz, that means that I have to switch the coil at least 2400 times per second (4 coils * 600 Hz). That means, that I have to measure the angle at least that often, more would be better. The faster I can calculate the angle, the better I can switch the coil in the right moment. Lets say I need to measure the angle 7200 times per second, that means I do have less that 138 µs per calculation. Since reading adc can take about 100µs this seems to be a hard job.
Please tell me if I don't get that right :)

But you are setting out for hacking a C program now. And it's a huge and complex C program -- the micropython interpreter.


Yes, you're right. I thought it might be easier to add one function without changing big parts of the programm. Maybe my perception about this was a little bit too optimistic.
The best way would be to be able to set the ad converter to a faster mode, which must be possible in theory since read_timed does read the values much faster but just doesn't allow me to do something in between.

Yours Sincerely,
Felix

User avatar
pythoncoder
Posts: 1381
Joined: Fri Jul 18, 2014 8:01 am

Re: Read ADC without reinitializing channel

Postby pythoncoder » Sat Feb 25, 2017 11:09 am

That's a fast motor, so I see your problem. The MicroPython ADC read functions are rather limited, notably the fact that read_time blocks. Also it can't read more than one ADC concurrently which makes phase measurements of fast signals difficult.

One possible solution might be to investigate the stm module which enables direct access to the MCU registers. If you need even more speed there is support for inline assembler. I would look into those approaches before resorting to modifying MicroPython. Note that I haven't investigated stm in the context of ADC's but I have used it to good effect in other roles.

On a more general topic do you actually need the position feedback? In many applications a stepper motor can be driven open loop. The main reason for closed loop control is in cases where the mechanical load (inertial and frictional) is unknown at runtime. If it is known and constant you can usually ramp up the speed at a predetermined rate. But you probably already know this ;)
Peter Hinch


Return to “The MicroPython pyboard”

Who is online

Users browsing this forum: Bing [Bot] and 3 guests