-
Notifications
You must be signed in to change notification settings - Fork 2.1k
cpu/stm32: unify i2c drivers and apply new API #9202
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
Conversation
@@ -159,6 +159,7 @@ static const spi_conf_t spi_config[] = { | |||
#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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. |
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.
A first batch of comments, I didn't go through everything yet.
cpu/stm32_common/periph/i2c.c
Outdated
*/ | ||
|
||
/** | ||
* @ingroup cpu_stm32l0 |
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
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is not the expected behavior, you should clear with RCC_CFGR3_I2C1SW
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 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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).
cpu/stm32_common/periph/i2c.c
Outdated
|
||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this could be factored in the CPU_FAM_STM32F0
block
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'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.
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.
Fixed
cpu/stm32_common/periph/i2c.c
Outdated
i2c->CR2 |= I2C_CR2_STOP; | ||
} | ||
|
||
static inline void irq_handler(i2c_t dev) |
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 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. |
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.
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) |
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.
length should be size_t
cpu/stm32_common/periph/i2c.c
Outdated
|
||
uint16_t tick = TICK_TIMEOUT; | ||
|
||
for (int i = 0; i < length; i++) { |
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.
size_t
cpu/stm32_common/periph/i2c.c
Outdated
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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) |
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.
size_t
cpu/stm32_common/periph/i2c.c
Outdated
|
||
uint16_t tick = TICK_TIMEOUT; | ||
|
||
for (int i = 0; i < length; i++) { |
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.
size_t
cpu/stm32_common/periph/i2c.c
Outdated
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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