-
Notifications
You must be signed in to change notification settings - Fork 288
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for MicroBitI2C.set/get_frequency(). #296
Conversation
The frequency is clamped and stored in the mbed I2C object, but the set function returns void and the storage is protected (and neither mbed nor DAL provides a getter). I work around this with our own copy of the frequency and our own validation code that raises ValueError.
Last time I tried to do this, on this exact way, the frequency wasn't changing. So from that experience I assumed the DAL would have to be updated to expose their internal I2C object frequency method, but micropython is currently using an old version, and it would have to be retroactively patched. Because of that the fact that it would have to go from version 1.4.15 to probably 1.4.18 and I didn't know if there were other unwanted changes in-between I didn't really get around going that route. I'll try to test your PR tonight just in case anything I might have just made a mistake on my version. |
The DAL has no direct concept of I2C frequency. The frequency method I call is a public method directly from the mbed API's |
I know, that's why I was so surprised it didn't work when I tried it from micropython, maybe I made a mistake somewhere though, it was the first time I was playing around with the code. Worked fine when I added the public method on the DAL and called that though. I'm on my way home now, I have a microbit all set up there, so I could soon get some results from this patch. |
Thank you for the test. I wonder if I should get a portable logic analyzer/scope... |
At home I use a DSO, don't really have the space for a lab and can usually stay late at work if I need to use some proper equipment, but it's been really useful, I do recommend it. |
As a general note, I'm not a big fan of having a get function for a locally saved value of what we expect the frequency to be. If any of the lower level layers changes the behaviour, or a different component decides to edit the frequency for whatever reason, then you'd be reporting an incorrect value. |
@ntoll and @markshannon concur. |
If there is not |
@@ -111,7 +141,7 @@ const mp_obj_type_t microbit_i2c_type = { | |||
|
|||
const microbit_i2c_obj_t microbit_i2c_obj = { | |||
{µbit_i2c_type}, | |||
.i2c = &uBit.i2c | |||
.i2c = &uBit.i2c, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can revert the command the line would then be unchanged.
I feels like it should be a property if it's named |
Good point, also there are plenty of get_ functions, so good to follow convention. |
The uart object uses the init() method to set parameters like baudrate, and the I2C object should follow suit, eg:
|
The SPI object also uses init(). UART and SPI both use "baudrate" for their clock speed. |
Using |
@@ -132,7 +132,7 @@ STATIC const mp_doc_t help_table_instances[] = { | |||
{µbit_pin_is_touched_obj, "If pin is_touched() on micro:bit, return True. If nothing is touching the pin,\nreturn False.\n"}, | |||
// I2C | |||
{µbit_i2c_obj, "Communicate with one or more named devices connected to micro:bit. Each named\ndevice has an 'address', communicates using I2C, and connects to the I/O pins.\n"}, | |||
{µbit_i2c_set_frequency_obj, "Use set_frequency(f) to set the I2C bus frequency.\n"}, | |||
{µbit_i2c_init_obj, "Use init() to set up communication with a clock frequench of 100 KHz.\nOverride the default for 'frequency' in Hz.\n"}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I quite like the spi init description Use init() to set up communication. Override the defaults for baudrate, mode,\nSCLK, MOSI and MISO. The default connections are pin13 for SCLK, pin15 for\nMOSI and pin14 for MISO.\n
, makes it clear, in a concise form, that you can call without arguments or overwrite defaults. So maybe something slightly shorter like?:
Use init() to set up communication. Override the 100KHz default for 'frequency' with a value in Hz."
So, if I have two devices on the bus (i2c or spi) that both require different clock frequencies, how do I switch between them without calling init() I raise this, because I designed a product a couple of years ago (that is now successfully deployed in the field) that had a number of devices on the bus, each with different max freq requirements. To get the necessary performance out of the bus (so that the top loop worked in a sensible time and all sensors were acquired in a deadline), I had to live shift the clock frequency of the bus. Do you think that calling init() regularly round a top loop will work fine in this scenario? Some A2D converters for example have clock speed requirements that if not met, the on board charge reservoir capacitors aren't correctly maintained and you get linearisation errors in the readings across the scale. |
At the moment for I2C the init function basically calls the c++ i2c frequency method, so you can safely call it at any point to switch frequencies and it should not affect any other properties of the peripheral. However, if I am not mistaken, the DAL will be continuously updating things on the background that are using the i2c communication, so it that will obviously be affected by this. |
As a continuation, I think the background updates are possibly only triggered the first time the DAL object is accessed from your program. So, for instance, if the microbit.accelerometer is never used, it should in theory not have this background updates running. But I've never really checked, so I might be wrong. |
@markshannon explained to me that micropython disables the DAL event loop and installs its own, and that the accelerometer is read on-demand. The compass is also generally read on-demand, but will be automatically polled if it is still calibrating. I believe that we then have implicit bus locking in that the accelerometer and compass are generally synchronous. The investigation of the clock frequency situation however is still valid (and not a problem (yet)).
However, as far as frequency is concerned, there is no changing (or setting) of the frequency in the DAL, and I could believe that the devices both support a 400 KHz bus, so for now we should be safe. Inspection of the datasheets for the two components shows that this is in fact true. |
You can't switch the bus frequencies to talk to different devices. From the I2C specification: Fast-mode devices are downward-compatible and can communicate with So it doesn't make sense to switch the I2C bus speed more than once, at the On Sat, 04 Jun 2016 02:08:53 -0700
Radomir Dopieralski |
You can't switch the bus frequencies to talk to different devices. From the I2C specification: Fast-mode devices are downward-compatible and can communicate with So it doesn't make sense to switch the I2C bus speed more than once, at On Sat, 04 Jun 2016 02:08:53 -0700
Radomir Dopieralski Radomir Dopieralski |
Sure, that is true about I2C you are correct. But there is often a requirement to speed switch on SPI where multiple devices are in use. It might be clearer for there to be a consistent API across both of the busses? Or at least to make it possible to speed switch SPI live. |
Yes this is definitely true. Also changing the polarity/phase of the SPI bus between transfers is just as necessary as changing the frequency.
Using an init() method makes it consistent across UART, SPI and I2C. But we'd need to define the init() function to only update the given parameters (eg spi.init(phase=0) doesn't change anything except the phase). Unfortunately this is in conflict with how uart.init() works, which resets everything to the default if it's not specified as an argument. |
I personally find the current UART init behaviour a bit more intuitive due to the method name itself and the fact that the documentation clearly states defaults. If the function was called something like "configure" or "setup" I think I could assume that |
I think we need to have a broader discussion wrt setting parameters, to make sure things are consistent. Things that are configurable are:
It might make sense for some of these parameters to also have a "get", but probably not all of them. Currently UART and SPI have an One way to proceed is provide a generic Another way is to provide an individual method for each value to set (and perhaps get), eg |
@dpgeorge I complete agree with that we should have a proper discussion to unify these configuration APIs, specially now that have a few pull requests that would be affected. However as this touches quite a few modules, it should go into a new issue and then have each PR linked to that. So I hope you don't mind that I have opened #306 for this. |
i2c.init() was added a while ago in e88e3b2, and it can be used to set the I2C frequency. |
The frequency is clamped and stored in the mbed I2C object, but the set
function returns void and the storage is protected (and neither mbed nor DAL
provides a getter). I work around this with our own copy of the frequency and
our own validation code that raises ValueError.
I don't have equipment at PyCon to test this. Someone please make sure this works. The three frequencies that need to be tested are 100 KHz, 250 KHz, and 400 KHz.
Resolves #228