-
Notifications
You must be signed in to change notification settings - Fork 2k
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
cpu/stm_common: Refactor and fix implementation for i2c_2 #10608
Conversation
c1a5609
to
52ee76c
Compare
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.
cpu/stm32_common/include/periph_cpu_common.h | 5 +
cpu/stm32_common/periph/i2c_2.c | 383 ++++++++++-----------------
2 files changed, 143 insertions(+), 245 deletions(-)
Very nice! :-)
_i2c_init(i2c, i2c_config[dev].clk, ccr); | ||
|
||
#if defined(CPU_FAM_STM32F4) | ||
/* make sure the analog filters don't hang -> see errata sheet 2.14.7 */ | ||
if (i2c->SR2 & I2C_SR2_BUSY) { | ||
DEBUG("[i2c] init: line busy after reset, toggle pins now\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.
Why do you remove all the DEBUG()
messages from i2c_init()
? They don't produce any machine code unless ENABLE_DEBUG
is set to true. So I don't see any harm in having them, and they could be extremely valuable when hunting a bug during I2C initialization.
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.
This is because if you do enable debug it doesn't actually work and seems to screw up the shell, I think it has something to do with the uart being initialized after the i2c but I didn't really look into it. I wrote something above the enable debug to indicate that. It may only have been for the nucleo-f103rb though.
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 think the problem is also there with F4 and the workaround could be, as you noticed, to initialize uart before i2c. That's what I did when debugging this driver some months ago.
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.
It would be nice for debugging to have stdio working as soon as possible during boot. But that is unrelated to this PR. Keeping those DEBUG statements will bite the next person trying to debug I2C unless someone would change the boot up process to initialize stdio sooner. So I agree that removing them is the better alternative.
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.
Noted, I opened an issue #10614 to document this concern. I also did write that comment explaining it, so hopefully it would be more a nibble than a bite.
@@ -187,9 +187,6 @@ int i2c_release(i2c_t dev) | |||
{ | |||
assert(dev < I2C_NUMOF); | |||
|
|||
uint16_t tick = TICK_TIMEOUT; | |||
while ((i2c_config[dev].dev->SR2 & I2C_SR2_BUSY) && tick--) {} |
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.
Let my try to understand this.
- Previously
i2c_release()
would have forTICK_TIMEOUT = 0xffff
loop iterations for the device to become idle. If the devices remained busy, the bus was released anyway. i2c_release()
should only be called from the thread currently that has the mutex locked viai2c_acquire()
.- Any data transfer is done blocking, so the thread that acquired the device will only be able to call
i2c_release()
, when the bus is idle anyway- Thus, this loop should never spin at all and can be safely removed. Right?
And also: If the relatively slow I2C bus was busy doing some reasonable stuff (e.g. a data transfer), a delay of 0xffff
loop iterations won't be enough for that transfer to complete anyway. So there would be no benefit of waiting in any case, right?
If my understanding is right, I agree that removing those line is a good thing.
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.
If the bus was still transferring the delay should actually allow it to finish (depending on clock speeds). However, this implementation ensures that, when an i2c stop is issued, the function will only end if the bus is free. This removes the requirement to check if the line is busy before doing anything (which is needed for repeated start calls).
So yes, it is essentially not doing anything valuable (assuming the api is being followed correctly) and thus should be removed.
@maribu Thanks for the review, I did want some feedback on the removing of the busy timeout in the release function. It is a good clarification. |
I can confirm that the SHT3X driver (using I2C) on the bluepill (STM32F103) just works fine with your I2C implementation :-) The driver does not use every function the I2C does define (e.g. the convenience functions for register access are not used) - so this test does not cover all your code. |
Thank you very much for testing, I can also volunteer @leandrolanzieri for more test too 😆 |
if the stm32f1 fix gets merged I will have to rebase again on top of that. Just a note to self. |
52ee76c
to
b75c9b1
Compare
cpu/stm32_common/periph/i2c_2.c
Outdated
|
||
#define ENABLE_DEBUG (0) | ||
/* DEBUG statements in the init causes the code to lock up */ |
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.
This could be removed if #10615 is merged.
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.
Yup, will do if it is merged before this one, otherwise I will remove it with the other PR.
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.
@MrKevinWeiss any chance to remove this today? And then we can merge.
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.
Thought I forgot something, done!
Tested on nucleo-f410rb and works as intended:
|
cpu/stm32_common/periph/i2c_2.c
Outdated
clocked out on the line however they get ignored in the firmware.*/ | ||
if ((i2c->SR1 & I2C_SR1_RXNE) && (length == 1)) { | ||
((uint8_t*)data)[i] = i2c->DR; | ||
ret = _stop(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.
this can be made return _stop(i2c);
because ret
is returned anyway
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.
Agreed, the less lines the better
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.
Done
b75c9b1
to
32e2416
Compare
Can you provide the corresponding shell commands of
I'd like to give a try to this one on nucleo-l152re |
@aabadie The thing is you need a sensor or something to interface to. If you have a nucleo-103rb you can flash the PHiLIP firmware, drag and drop the firmware and hook up the scl and sda pins. If you want to use a sensor then let me know what it is and I can tell you the commands. |
We can generalize certain things for PHiLIP = 85 and = 152, for a sensor is the address of the sensor and would be a register in the sensor (preferable a few) that is readable and writable. Then we could go:
That is the basics of it. There is also a test.py in the folder but to use it you should
|
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.
Tested on a nucleo-l152re and it works. I found small code style issues and I also had an assertion error with one of the commands you gave in your previous comment. See below.
cpu/stm32_common/periph/i2c_2.c
Outdated
static inline int _stop(I2C_TypeDef *dev); | ||
static inline int _wait_ready(I2C_TypeDef *dev); | ||
static int _start(I2C_TypeDef *dev, uint8_t address_byte, uint8_t flags, | ||
size_t length); |
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.
Code style: this should be aligned with I2C_TypeDef
.
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.
yup yup.
cpu/stm32_common/periph/i2c_2.c
Outdated
int i2c_write_regs(i2c_t dev, uint16_t address, uint16_t reg, const void *data, | ||
size_t length, uint8_t flags) | ||
static int _start(I2C_TypeDef *i2c, uint8_t address_byte, uint8_t flags, | ||
size_t length) |
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.
Code style: this should be aligned with I2C_TypeDef
.
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.
Thanks will do
cpu/stm32_common/periph/i2c_2.c
Outdated
assert(i2c != NULL); | ||
assert((i2c->SR2 & I2C_SR2_BUSY) || !(flags & I2C_NOSTART)); |
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 got a failed assertion on this line when brainlessly testing using tests/periph_i2c
on nucleo-l152re
with an hts221 sensor plugged. With the following commands list:
> i2c_acquire 0
> i2c_read_bytes 0 0x5f 1 12
as suggested in #10608 (comment)
Would it make more sense to have a return with a dedicated error value instead of crashing the application in this case ?
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.
Yup I don't mind that, I was trying to keep the binary size down but I can see how that would be annoying when using it (it happened to me a few times)
32e2416
to
8e782d2
Compare
@aabadie All your comments have been addressed! |
cpu/stm32_common/periph/i2c_2.c
Outdated
|
||
if ((flags & I2C_REG16) || (flags & I2C_ADDR10)) { | ||
if ((flags & I2C_ADDR10) || | ||
(!(i2c->SR2 & I2C_SR2_BUSY) && (flags & I2C_NOSTART)) { |
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.
missing )
here, gives compilation error
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.
...shhhhhh, nothing happened here:sweat_smile:
8e782d2
to
8a5069c
Compare
Refactors i2c_2 to match the structure of i2c_1 better. Corrects functionality issues. Allows the common implementation of read_regs and write_regs. Documents constraints of hardware. Matches error messages with API.
8a5069c
to
a2e059d
Compare
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.
Reviewed and tested. Thanks for updating @MrKevinWeiss.
ACK
Thanks to everyone for reviewing, testing & merging! |
Contribution description
This PR fixes many bugs discovered by the HiL.
Testing procedure
Test on stm32f1 f2 f4 or l1 board
One can run the robot-test from the HiL branch or run term in tests/periph_i2c/
Or run manually against your favorite i2c device. No everything will be supported but everything should at least give correct error messages and not lock up the bus.
*If nucleo-f103rb is used it needs external pullups.
Issues/PRs references
helps with some stm32 based boards for #3366
checks some boxes in #9518