cpu/stm32: unify i2c drivers and apply new API#9202
cpu/stm32: unify i2c drivers and apply new API#9202dylad merged 17 commits intoRIOT-OS:new_i2c_iffrom
Conversation
| #define I2C_NUMOF I2C_0_EN | ||
| #define I2C_IRQ_PRIO 1 | ||
| #define I2C_APBCLK (CLOCK_APB1) | ||
| #define I2C_SPEED I2C_SPEED_NORMAL |
There was a problem hiding this comment.
I think it would be great to take advantage of the refactoring to get rid of this "old-style" define way and use an array of struct as it's done for other periph.
| gpio_mode_t pin_mode; /**< with or without pull resistor */ | ||
| gpio_af_t af; /**< I2C alternate function value */ | ||
| uint8_t ev_irqn; /**< event IRQ */ | ||
| } i2c_conf_t; |
There was a problem hiding this comment.
Good to have this, but it seems unused, isn't it?
| gpio_t scl; /**< scl pin number */ | ||
| gpio_t sda; /**< sda pin number */ | ||
| gpio_mode_t pin_mode; /**< with or without pull resistor */ | ||
| gpio_af_t af; /**< I2C alternate function value */ |
There was a problem hiding this comment.
Don't you need an af per pin (e.g. one for sda, and one for scl)?
|
Current status of this PR:
|
d9e446a to
e68abff
Compare
Just found the problem ! Now both are working, will push a fix. In fact, the I2C clock source has to be turned on in one RCC register (CFGR3). |
9f3c503 to
d94ec5b
Compare
|
This PR is ready for review again. Actual status: tested nucleo-f070/f303/l073/f722, b-l072z-lrwan1 and b-l475e-iot01a boards with success. |
d94ec5b to
45c2b8f
Compare
|
There's a problem with Travis: this PR conflicts with current master (because of the DMA changes in the STM32 periph_common header file). |
|
Quickly tested with nucleo-f091 (see aabadie#7). It seems OK so far. |
vincent-d
left a comment
There was a problem hiding this comment.
A first batch of comments, I didn't go through everything yet.
cpu/stm32_common/periph/i2c.c
Outdated
| */ | ||
|
|
||
| /** | ||
| * @ingroup cpu_stm32l0 |
cpu/stm32_common/periph/i2c.c
Outdated
|
|
||
| #define I2C_IRQ_PRIO (1) | ||
|
|
||
| #define CLEAR_FLAG(dev) dev->ICR |= I2C_ICR_NACKCF | \ |
There was a problem hiding this comment.
we usually avoid macro, I would suggest to use a constant to only define the value,
i.e. #define CLEAR_FLAG (I2C_ICR_NACKCF | I2C_ICR_ARLOCF | I2C_ICR_BERRCF)
and then use it dev->ISR |= CLEAR_FLAG
There was a problem hiding this comment.
Another method which will clear all w1c flags is
dev->ISR = dev->ISR;
cpu/stm32_common/periph/i2c.c
Outdated
|
|
||
| #if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32F3) | ||
| /* Clear I2CSW bit */ | ||
| RCC->CFGR3 &= ~(i2c_config[dev].rcc_sw_mask); |
There was a problem hiding this comment.
I think this is not the expected behavior, you should clear with RCC_CFGR3_I2C1SW
There was a problem hiding this comment.
This is what is done. But the macro used for setting the clock after is not correct. I'll update this but that will required another field in the configuration struct.
There was a problem hiding this comment.
It works only when rcc_sw_mask is 1. This line will write 0, and the next one 1. If rcc_sw_mask is 0, it will leave the field as-is (so if this was the reset value it works, but then this line is useless).
|
|
||
| DEBUG("[i2c] init: configuring pins\n"); | ||
| /* configure pins */ | ||
| gpio_init(i2c_config[dev].scl_pin, GPIO_OD_PU); |
There was a problem hiding this comment.
Not sure if GPIO_OD_PU is the best here, same for sda. Why not using GPIO_OUT?
cpu/stm32_common/periph/i2c.c
Outdated
| return length; | ||
| } | ||
|
|
||
| void i2c_poweron(i2c_t dev) |
There was a problem hiding this comment.
i2c_poweron and i2c_poweroff are not in the API anymore
cpu/stm32_common/periph/i2c.c
Outdated
| periph_clk_dis(i2c_config[dev].bus, i2c_config[dev].rcc_mask); | ||
| } | ||
|
|
||
| static inline void _start(I2C_TypeDef *i2c, uint8_t address, |
There was a problem hiding this comment.
Even though 10-bit address is not yet implemented, I think it would be better to use uint16_t for address. Maybe pass the flags to be able to enable this feature later on.
|
|
||
| static const i2c_timing_param_t timing_params[] = { | ||
| #if defined(CPU_FAM_STM32F0) | ||
| [ I2C_SPEED_NORMAL ] = { |
There was a problem hiding this comment.
These timings are valid for an input clock wt 48MHz (same for L4 and F7, btw).
The clock requirements should be stated clearly somewhere (alongside the i2c_conf_t definition I guess).
cpu/stm32_common/periph/i2c.c
Outdated
| DEBUG("[i2c] init: configuring device\n"); | ||
| /* set the timing register value from predefined values */ | ||
| i2c_timing_param_t tp = timing_params[i2c_config[dev].speed]; | ||
| uint32_t timing = (( (uint32_t)tp.presc << 28) | |
There was a problem hiding this comment.
You might use the constants from the vendor header for the shift values
|
Thanks for reviewing @vincent-d, I'll update the PR asap |
|
@vincent-d, I think I addressed your comments. Regarding the pin configuration, even not a good reason, it kept what was set for f3 and l0. Looking at the reference manual didn't help. |
vincent-d
left a comment
There was a problem hiding this comment.
Still some small questions/remarks. Looks pretty good otherwise.
| .sdadel = 0x0, /* t_SDADEL = 0ns */ | ||
| .scldel = 0x1, /* t_SCLDEL = 250ns */ | ||
| } | ||
| #elif defined(CPU_FAM_STM32L4) || defined(CPU_FAM_STM32F7) |
There was a problem hiding this comment.
this could be factored in the CPU_FAM_STM32F0 block
There was a problem hiding this comment.
I'm also wondering if we should not group by frequency instead of by family. But this could be done later on if we think it makes more sense.
| i2c->CR2 |= I2C_CR2_STOP; | ||
| } | ||
|
|
||
| static inline void irq_handler(i2c_t dev) |
There was a problem hiding this comment.
It seems no interrupt is enable, is this function really needed or is this more for a future use?
There was a problem hiding this comment.
One interrupt is enabled here: https://github.com/RIOT-OS/RIOT/pull/9202/files#diff-8703624eb1944c61de7e7320bf02f6e3R80
On certain families (F3, L4, F7) there are 2 per I2C (ev/er), I only took the er for the moment.
I'd say let's start with that and fix later if this is an issue. My only concern is when an external pull-up is already there. |
vincent-d
left a comment
There was a problem hiding this comment.
Found a couple of small other issues, hopefully the last ones!
cpu/stm32_common/periph/i2c.c
Outdated
| while (!(i2c->CR2 & I2C_CR2_START) && tick--) {} | ||
| } | ||
|
|
||
| static inline int _read(I2C_TypeDef *i2c, uint8_t *data, int length) |
cpu/stm32_common/periph/i2c.c
Outdated
|
|
||
| uint16_t tick = TICK_TIMEOUT; | ||
|
|
||
| for (int i = 0; i < length; i++) { |
| for (int i = 0; i < length; i++) { | ||
| /* wait for transfer to finish */ | ||
| DEBUG("[i2c] read: Waiting for DR to be full\n"); | ||
| while (!(i2c->ISR & I2C_ISR_RXNE) && tick--) { |
There was a problem hiding this comment.
tick should be reset to TICK_TIMEOUT before
cpu/stm32_common/periph/i2c.c
Outdated
| return 0; | ||
| } | ||
|
|
||
| static inline int _write(I2C_TypeDef *i2c, const uint8_t *data, int length) |
cpu/stm32_common/periph/i2c.c
Outdated
|
|
||
| uint16_t tick = TICK_TIMEOUT; | ||
|
|
||
| for (int i = 0; i < length; i++) { |
| for (int i = 0; i < length; i++) { | ||
| /* wait for ack */ | ||
| DEBUG("[i2c] write: Waiting for ACK\n"); | ||
| while (!(i2c->ISR & I2C_ISR_TXIS) && tick--) { |
There was a problem hiding this comment.
tick should be reset to TICK_TIMEOUT before
|
@vincent-d, comments addressed! |
- i2c_1 is built for f0, f3, f7, l0 and l4 - i2c_2 is built for f4
5275fa9 to
d81ce15
Compare
d81ce15 to
4aa44dc
Compare
|
The rebase issue is fixed, some commits were not at the right place... |
|
Comments addressed @dylad |
|
@aabadie Travis reported an error with cppcheck. |
8209dfb to
fafec43
Compare
|
@dylad, finally fixed. |
|
Great job @aabadie |
|
@aabadie Some of your files now introduced merge conflict with master again. We will need to fix it somehow later |
cpu/stm32: unify i2c drivers and apply new API
cpu/stm32: unify i2c drivers and apply new API
cpu/stm32: unify i2c drivers and apply new API
Contribution description
This PR unifies the I2C peripheral driver implementation for STM32 F0/F3/F7/L0/L4 families, since they are very similar and adapt the related board configurations.
This PR also changes the way i2c peripheral configurations are provided by the boards. Now each board must define a nice array of structs, like for other peripherals.
For F0, F7 and L4, I added a default configuration for the nucleo-f070rb, nucleo-f722ze, and b-l475e-iot01a boards.
I tested it on b-l072z-lrwan1 and b-l475e-iot01a with a hts221 sensor and it works. I couldn't really test the F3 driver, since the driver seems broken, even in master. I couldn't find a solution for this. Same for F0.
For F3, I also had to update the f303xe cmsis file, and also found an issue in the periph_common init. Will open a PR for this.
Issues/PRs references
#6577