Skip to content

Conversation

aabadie
Copy link
Contributor

@aabadie aabadie commented May 25, 2018

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

@aabadie aabadie added Platform: ARM Platform: This PR/issue effects ARM-based platforms Area: drivers Area: Device drivers TF: I2C Marks issues and PRs related to the work of the I²C rework task force labels May 25, 2018
@@ -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
Copy link
Member

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;
Copy link
Member

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 */
Copy link
Member

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)?

@aabadie aabadie changed the title Unify stm32 L0 and F3 I2C implementation and apply new API Unify STM32 F0/F3/L0 i2c driver and apply new API May 26, 2018
@aabadie aabadie changed the title Unify STM32 F0/F3/L0 i2c driver and apply new API Unify STM32 F0/F3/L0/L4 i2c driver and apply new API May 26, 2018
@aabadie
Copy link
Contributor Author

aabadie commented May 26, 2018

Current status of this PR:

  • L0: works, tested on b-l072z-lrwan
  • L4: works, tested on b-l475e-iot01a
  • F7: works, tested on nucleo144-f722
  • F0 and F3 are broken. For both, the application hangs before sending the start condition (more precisely, in the function that does that, while configuring the registers). I couldn't find a reason why this happens and gdb didn't help either.

@aabadie aabadie changed the title Unify STM32 F0/F3/L0/L4 i2c driver and apply new API Unify STM32 F0/F3/F7/L0/L4 i2c driver and apply new API May 26, 2018
@aabadie aabadie force-pushed the new_i2c_stm32l0_f3 branch from d9e446a to e68abff Compare May 27, 2018 14:01
@aabadie
Copy link
Contributor Author

aabadie commented May 28, 2018

F0 and F3 are broken

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).

@aabadie aabadie force-pushed the new_i2c_stm32l0_f3 branch 3 times, most recently from 9f3c503 to d94ec5b Compare May 28, 2018 08:15
@aabadie
Copy link
Contributor Author

aabadie commented May 28, 2018

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.
So now I2C works for all STM32 F0/F3/F7/L0/L4 cpu families.

@aabadie aabadie force-pushed the new_i2c_stm32l0_f3 branch from d94ec5b to 45c2b8f Compare May 28, 2018 08:18
@aabadie
Copy link
Contributor Author

aabadie commented May 28, 2018

There's a problem with Travis: this PR conflicts with current master (because of the DMA changes in the STM32 periph_common header file).
What should we do in this case ? Rebase new_i2c_if on top of master (with or without --preserve-merges option) ? @miri64 did you have the same issue with the netif2 feature branch ? (how was this solved ?) Sorry, lots of questions.

@vincent-d
Copy link
Member

Quickly tested with nucleo-f091 (see aabadie#7). It seems OK so far.

Copy link
Member

@vincent-d vincent-d left a 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.

*/

/**
* @ingroup cpu_stm32l0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cpu_stm32_common


#define I2C_IRQ_PRIO (1)

#define CLEAR_FLAG(dev) dev->ICR |= I2C_ICR_NACKCF | \
Copy link
Member

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

Copy link
Member

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;


#if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32F3)
/* Clear I2CSW bit */
RCC->CFGR3 &= ~(i2c_config[dev].rcc_sw_mask);
Copy link
Member

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

Copy link
Contributor Author

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.

Copy link
Member

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).


DEBUG("[i2c] init: configuring pins\n");
/* configure pins */
gpio_init(i2c_config[dev].scl_pin, GPIO_OD_PU);
Copy link
Member

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?

return length;
}

void i2c_poweron(i2c_t dev)
Copy link
Member

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

periph_clk_dis(i2c_config[dev].bus, i2c_config[dev].rcc_mask);
}

static inline void _start(I2C_TypeDef *i2c, uint8_t address,
Copy link
Member

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 ] = {
Copy link
Member

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).

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) |
Copy link
Member

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

@aabadie
Copy link
Contributor Author

aabadie commented May 28, 2018

Thanks for reviewing @vincent-d, I'll update the PR asap

@aabadie
Copy link
Contributor Author

aabadie commented May 28, 2018

@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.

Copy link
Member

@vincent-d vincent-d left a 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)
Copy link
Member

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

Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

i2c->CR2 |= I2C_CR2_STOP;
}

static inline void irq_handler(i2c_t dev)
Copy link
Member

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?

Copy link
Contributor Author

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.

@vincent-d
Copy link
Member

Regarding the pin configuration, even not a good reason, it kept what was set for f3 and l0.

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.

Copy link
Member

@vincent-d vincent-d left a 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!

while (!(i2c->CR2 & I2C_CR2_START) && tick--) {}
}

static inline int _read(I2C_TypeDef *i2c, uint8_t *data, int length)
Copy link
Member

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


uint16_t tick = TICK_TIMEOUT;

for (int i = 0; i < length; i++) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

size_t

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--) {
Copy link
Member

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

return 0;
}

static inline int _write(I2C_TypeDef *i2c, const uint8_t *data, int length)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

size_t


uint16_t tick = TICK_TIMEOUT;

for (int i = 0; i < length; i++) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

size_t

for (int i = 0; i < length; i++) {
/* wait for ack */
DEBUG("[i2c] write: Waiting for ACK\n");
while (!(i2c->ISR & I2C_ISR_TXIS) && tick--) {
Copy link
Member

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

@aabadie
Copy link
Contributor Author

aabadie commented May 28, 2018

@vincent-d, comments addressed!

@aabadie aabadie force-pushed the new_i2c_stm32l0_f3 branch from 5275fa9 to d81ce15 Compare June 13, 2018 11:12
@aabadie aabadie force-pushed the new_i2c_stm32l0_f3 branch from d81ce15 to 4aa44dc Compare June 13, 2018 11:19
@aabadie
Copy link
Contributor Author

aabadie commented Jun 13, 2018

The rebase issue is fixed, some commits were not at the right place...

@aabadie
Copy link
Contributor Author

aabadie commented Jun 13, 2018

Comments addressed @dylad

@dylad
Copy link
Member

dylad commented Jun 13, 2018

@aabadie Travis reported an error with cppcheck.
Squashed directly so we can merge this one.

@aabadie aabadie force-pushed the new_i2c_stm32l0_f3 branch from 8209dfb to fafec43 Compare June 13, 2018 12:15
@aabadie
Copy link
Contributor Author

aabadie commented Jun 13, 2018

@dylad, finally fixed.

@dylad
Copy link
Member

dylad commented Jun 13, 2018

Great job @aabadie
Here we go !

@dylad dylad merged commit 7c021da into RIOT-OS:new_i2c_if Jun 13, 2018
@dylad
Copy link
Member

dylad commented Jun 13, 2018

@aabadie Some of your files now introduced merge conflict with master again. We will need to fix it somehow later

@aabadie aabadie deleted the new_i2c_stm32l0_f3 branch June 21, 2018 09:31
basilfx pushed a commit to basilfx/RIOT that referenced this pull request Jul 10, 2018
cpu/stm32: unify i2c drivers and apply new API
dylad added a commit to dylad/RIOT that referenced this pull request Jul 10, 2018
cpu/stm32: unify i2c drivers and apply new API
dylad added a commit that referenced this pull request Jul 11, 2018
cpu/stm32: unify i2c drivers and apply new API
@aabadie aabadie added this to the Release 2018.10 milestone Nov 5, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: drivers Area: Device drivers Platform: ARM Platform: This PR/issue effects ARM-based platforms TF: I2C Marks issues and PRs related to the work of the I²C rework task force
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants