Hi,
I am new to github, and writing i2c drivers so I need some help.
I am writing a driver for the AS5600. I have made all the registers and bitfields in data sheet attributes of a class.
There is an early example of a higher level class that inherits from above, with a simple useage example.
I am using a Raspberry Pi Pico with
Version of micropython rp2-pico-20210704-unstable-v1.16-52-g0e11966ce.uf2
Programming on Thonny.
The board is a Seeedstudio board with Grove connnection.
Github:- https://github.com/sgall17a/AS5600/rele ... v0.1-alpha
There are some problems (relating to i2c I think ) that I need help with:
1. Reading ANGLE and RAWANGLE seem to work OK.
2. CONF (configure register) is writable but I dont understand its behaviour in my program.
If I write a value (any value) and read immediately it comes back zero.
If I power on and off the value written is there.
If I read and print the value and rewrite and reread without repowering it does not change.
4. I am using readto_mem and write_to mem I2C methods. Could these be the problem?
5. The data sheet sheet says that address autoincrement on reading (for most registers). Could this be the problem?
6. Problem 2.
The STATUS register does not seem to recognise a magnet even though it will read angles OK, so it must be seeing a magnet.
Is it OK to post blocks of code here or just refer to the github source?
AS5600 magnetic rotary sensor
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: AS5600 magnetic rotary sensor
I notice your code uses descriptors. In this official doc there is a caveat about using descriptors:
However I don't think that is the cause of your problem and I'm afraid I don't have an answer to that one.
Your use of GitHub isn't optimal: it's best to post your source code directly rather than in a zip file, as then you get the benefit of Git version control.
You're welcome to post code or GitHub links on this forum.
This also extends to properties. These work, but are deprecated in applications requiring high performance.MicroPython design is not based around descriptor objects. So far, we were able to implement all native Python features (like properties) without explicit descriptors. Descriptors are considered "overdynamic" feature, and conflicts with the aim of being fast and efficient. However, basic support for descriptors in user-defined classes is now implemented.
However I don't think that is the cause of your problem and I'm afraid I don't have an answer to that one.
Your use of GitHub isn't optimal: it's best to post your source code directly rather than in a zip file, as then you get the benefit of Git version control.
You're welcome to post code or GitHub links on this forum.
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.
Re: AS5600 magnetic rotary sensor
Thanks Pythoncoder,
Why I liked descriptors in this setting:
1. I think Attributes are simple and intuitive use and avoid exposing separate getter and setter functions to the user.
2. I tried using properties but these (at least in my implementation) where much more verbose and actually harder to program.
3. Descriptors permit "boilerplate" like code which is easy to follow.
4. They may well be a bit slower but who cares for things like configuration?
5. For something like reading a whole lot of angles quickly you can still write a dedicated "fast read".
6. I dont really know about the internal implications of descriptors but wouldn't be less verbose also mean smaller and more space efficient?
My problem was easily fixed (after a short walk in the brisk winter air), I was not writing back to my cache properly.
I also realised that the cache should belong to the main class rather than the descriptor
so that different descriptors could share the value.
In hindsight I think nametuples were silly. I was just trying them out. I think just a series of micropython constants would be better.
Why I liked descriptors in this setting:
1. I think Attributes are simple and intuitive use and avoid exposing separate getter and setter functions to the user.
2. I tried using properties but these (at least in my implementation) where much more verbose and actually harder to program.
3. Descriptors permit "boilerplate" like code which is easy to follow.
4. They may well be a bit slower but who cares for things like configuration?
5. For something like reading a whole lot of angles quickly you can still write a dedicated "fast read".
6. I dont really know about the internal implications of descriptors but wouldn't be less verbose also mean smaller and more space efficient?
My problem was easily fixed (after a short walk in the brisk winter air), I was not writing back to my cache properly.
I also realised that the cache should belong to the main class rather than the descriptor
so that different descriptors could share the value.
In hindsight I think nametuples were silly. I was just trying them out. I think just a series of micropython constants would be better.
Code: Select all
from machine import I2C,Pin
from micropython import const
from ustruct import unpack, pack
from collections import namedtuple
from time import sleep
AS5600_id = const(0x36) #Device ID
m12 = const((1<<12)-1) #0xFFF
REGS=namedtuple('REGS','ZMCO ZPOS MPOS MANG CONF RAWANGLE ANGLE STATUS AGC MAGNITUDE BURN')
r = REGS(0,1,3,5,7,0xc,0xe,0x0b,0x1a,0xb,0xff)
#You cant overwrite __attribute__ in micropython but you can use Descriptors
class RegDescriptor:
"Read and write a bit field from a register"
def __init__(self,reg,shift,mask,buffsize=2):
"initialise with specific identifiers for the bit field"
self.reg = reg
self.shift = shift
self.mask = mask
self.buffsize = buffsize
self.writeable = (r.ZMCO,r.ZPOS,r.MPOS,r.MANG,r.CONF,r.BURN)
#NB the I2c object and the device name come from the main class via an object
def get_register(self,obj):
"Read an I2C register"
#cache those registers with values that will not change.
#Dont bother caching bit fields.
if self.reg in obj.cache:
return obj.cache[self.reg]
#print ('reading now the actual device now')
buff = obj.i2c.readfrom_mem(obj.device,self.reg,self.buffsize)
if self.buffsize == 2:
v = unpack(">H",buff)[0] #2 bytes big endian
else:
v = unpack(">B",buff)[0]
#cache writeable values since they are the ones that will not change in useage
if self.reg in self.writeable:
obj.cache[self.reg] = v
return v
def __get__(self,obj,objtype):
"Get the register then extract the bit field"
v = self.get_register(obj)
v >>= self.shift
v &= self.mask
return v
def __set__(self,obj,value):
"Insert a new value into the bit field of the old value then write it back"
if not self.reg in self.writeable:
raise AttributeError('Register is not writable')
oldvalue = self.get_register(obj)
#oldvalue <<= self.shift # get_register() does a shift, so we have to shift it back
insertmask = 0xffff - (self.mask << self.shift) #make a mask for a hole
oldvalue &= insertmask # AND a hole in the old value
value &= self.mask # mask our new value in case it is too big
value <<= self.shift
oldvalue |= value # OR the new value back into the hole
if self.buffsize == 2:
buff = pack(">H",oldvalue)
else:
buff = pack(">B",oldvalue)
obj.i2c.writeto_mem(obj.device,self.reg,buff) # write result back to the AS5600
#must write the new value into the cache
self.cache[self.reg] = oldvalue
class AS5600:
def __init__(self,i2c,device):
self.i2c = i2c
self.device = device
self.writeable =(r.ZMCO,r.ZPOS,r.MPOS,r.MANG,r.CONF,r.BURN)
self.cache = {} #cache register values
#Use descriptors to read and write a bit field from a register
#1. we read one or two bytes from i2c
#2. We shift the value so that the least significant bit is bit zero
#3. We mask off the bits required (most values are 12 bits hence m12)
ZMCO= RegDescriptor(r.ZMCO,shift=0,mask=3,buffsize=1) #1 bit
ZPOS= RegDescriptor(r.ZPOS,0,m12) #zero position
MPOS= RegDescriptor(r.MPOS,0,m12) #maximum position
MANG= RegDescriptor(r.MANG,0,m12) #maximum angle (alternative to above)
#Dummy example how how to make friendlier duplicate names if you want to
#max_angle = RegDescriptor(r.MANG,0,m12) #maximum angle (alternative to above)
CONF= RegDescriptor(r.CONF,0,(1<<14)-1) # this register has 14 bits (see below)
RAWANGLE= RegDescriptor(r.RAWANGLE,0,m12)
ANGLE = RegDescriptor(r.ANGLE,0,m12) #angle with various adjustments (see datasheet)
STATUS= RegDescriptor(r.STATUS,0,m12) #basically strength of magnet
AGC= RegDescriptor(r.AGC,0,0xF,1) #automatic gain control
MAGNITUDE= RegDescriptor(r.MAGNITUDE,0,m12) #? something to do with the CORDIC for atan RTFM
BURN= RegDescriptor(r.BURN,0,0xF,1)
#Configuration bit fields
PM = RegDescriptor(r.CONF,0,0x3) #2bits Power mode
HYST = RegDescriptor(r.CONF,2,0x3) # hysteresis for smoothing out zero crossing
OUTS = RegDescriptor(r.CONF,4,0x3) # HARDWARE output stage ie analog (low,high) or PWM
PWMF = RegDescriptor(r.CONF,6,0x3) #pwm frequency
SF = RegDescriptor(r.CONF,8,0x3) #slow filter (?filters glitches harder) RTFM
FTH = RegDescriptor(r.CONF,10,0x7) #3 bits fast filter threshold. RTFM
WD = RegDescriptor(r.CONF,13,0x1) #1 bit watch dog - Kicks into low power mode if nothing changes
#status bit fields. ?having problems getting these to make sense
MH = RegDescriptor(r.STATUS,3,0x1) #2bits Magnet too strong (high)
ML = RegDescriptor(r.STATUS,4,0x1) #2bits Magnet too weak (low)
MD = RegDescriptor(r.STATUS,5,0x1) #2bits Magnet detected
#Higher level stuff follows
def scan(self):
"Debug utility function to check your i2c bus"
devices = self.i2c.scan()
print(devices)
if AS5600_id in devices:
print('Found AS5600 (id =',hex(AS5600_id),')')
print(self.CONF)
def burn_angle(self):
"Burn ZPOS and MPOS -(can only do this 3 times)"
self.BURN = 0x80
def burn_setting(self):
"Burn config and mang- (can only do this once)"
self.BURN = 0x40
def magnet_status(self):
s = "Magnet "
# print(self.MD)
return
if self.MD == 1:
s += " detected"
else:
s += " not detected"
if self.ML == 1:
s+ " (magnet too weak)"
if self.MH == 1:
s+ " (magnet too strong)"
return s
Re: AS5600 magnetic rotary sensor
From the cPython documentation.
"Descriptors are used throughout the language. It is how functions turn into bound methods. Common tools like classmethod(), staticmethod(), property(), and functools.cached_property() are all implemented as descriptors."
I got the impression from this that descriptors is a fairly standard way of doing things. Is this not the case? Are things different in Micropython?
"Descriptors are used throughout the language. It is how functions turn into bound methods. Common tools like classmethod(), staticmethod(), property(), and functools.cached_property() are all implemented as descriptors."
I got the impression from this that descriptors is a fairly standard way of doing things. Is this not the case? Are things different in Micropython?
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: AS5600 magnetic rotary sensor
I am no expert on the internal implementation of MicroPython. The quote I posted above seems to state that MicroPython does not use descriptors internally. As you point out, CPython does, extensively.
I take your point about performance. I just thought it worth pointing out the MicroPython convention of using bound methods rather than properties or descriptors. If you look at the official libraries you will see that this is adopted throughout. However Adafruit take the opposite approach and use descriptors extensively. It is entirely your choice.
Constants are a great way to improve performance. You can also reduce RAM consumption by prepending names with an underscore.
I take your point about performance. I just thought it worth pointing out the MicroPython convention of using bound methods rather than properties or descriptors. If you look at the official libraries you will see that this is adopted throughout. However Adafruit take the opposite approach and use descriptors extensively. It is entirely your choice.
Constants are a great way to improve performance. You can also reduce RAM consumption by prepending names with an underscore.
Code: Select all
_MY_LOCAL_VALUE = const(42) # Save 4 bytes
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.
Re: AS5600 magnetic rotary sensor
Thanks - I will go through some other drivers and revise mine.