mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-23 08:06:13 +01:00
STM32 I2C Clock selection for 100/400KHz
This commit is contained in:
parent
4f43a413b5
commit
e51f8e9c0a
|
@ -26,34 +26,42 @@
|
||||||
#include "I2CManager.h"
|
#include "I2CManager.h"
|
||||||
#include "I2CManager_NonBlocking.h" // to satisfy intellisense
|
#include "I2CManager_NonBlocking.h" // to satisfy intellisense
|
||||||
|
|
||||||
//#include <avr/io.h>
|
|
||||||
//#include <avr/interrupt.h>
|
|
||||||
#include <wiring_private.h>
|
#include <wiring_private.h>
|
||||||
|
#include "stm32f4xx_hal_rcc.h"
|
||||||
|
|
||||||
/***************************************************************************
|
/*****************************************************************************
|
||||||
* Interrupt handler.
|
* STM32F4xx I2C native driver support
|
||||||
* IRQ handler for SERCOM3 which is the default I2C definition for Arduino Zero
|
*
|
||||||
* compatible variants such as the Sparkfun SAMD21 Dev Breakout etc.
|
* Nucleo-64 and Nucleo-144 boards all use I2C1 as the default I2C peripheral
|
||||||
* Later we may wish to allow use of an alternate I2C bus, or more than one I2C
|
* Later we may wish to support other STM32 boards, allow use of an alternate
|
||||||
* bus on the SAMD architecture
|
* I2C bus, or more than one I2C bus on the STM32 architecture
|
||||||
***************************************************************************/
|
*****************************************************************************/
|
||||||
#if defined(I2C_USE_INTERRUPTS) && defined(ARDUINO_ARCH_STM32)
|
#if defined(I2C_USE_INTERRUPTS) && defined(ARDUINO_ARCH_STM32)
|
||||||
|
#if defined(ARDUINO_NUCLEO_F411RE) || defined(ARDUINO_NUCLEO_F446RE) || defined(ARDUINO_NUCLEO_F412ZG) || defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE)
|
||||||
|
// Assume I2C1 for now - default I2C bus on Nucleo-F411RE and likely all Nucleo-64
|
||||||
|
// and Nucleo-144variants
|
||||||
|
I2C_TypeDef *s = I2C1;
|
||||||
|
|
||||||
|
// In init we will ask the STM32 HAL layer for the configured APB1 clock frequency in Hz
|
||||||
|
uint32_t APB1clk1; // Peripheral Input Clock speed in Hz.
|
||||||
|
uint32_t i2c_MHz; // Peripheral Input Clock speed in MHz.
|
||||||
|
|
||||||
|
// IRQ handler for I2C1, replacing the weak definition in the STM32 HAL
|
||||||
extern "C" void I2C1_EV_IRQHandler(void) {
|
extern "C" void I2C1_EV_IRQHandler(void) {
|
||||||
I2CManager.handleInterrupt();
|
I2CManager.handleInterrupt();
|
||||||
}
|
}
|
||||||
extern "C" void I2C1_ER_IRQHandler(void) {
|
extern "C" void I2C1_ER_IRQHandler(void) {
|
||||||
I2CManager.handleInterrupt();
|
I2CManager.handleInterrupt();
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
#warning STM32 board selected is not yet supported - so I2C1 peripheral is not defined
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Assume I2C1 for now - default I2C bus on Nucleo-F411RE and likely Nucleo-64 variants
|
|
||||||
I2C_TypeDef *s = I2C1;
|
|
||||||
#define I2C_IRQn I2C1_EV_IRQn
|
|
||||||
|
|
||||||
// Peripheral Input Clock speed in MHz.
|
// Peripheral Input Clock speed in MHz.
|
||||||
// For STM32F446RE, the speed is 45MHz. Ideally, this should be determined
|
// For STM32F446RE, the speed is 45MHz. Ideally, this should be determined
|
||||||
// at run-time from the APB1 clock, as it can vary from STM32 family to family.
|
// at run-time from the APB1 clock, as it can vary from STM32 family to family.
|
||||||
#define I2C_PERIPH_CLK 45
|
// #define I2C_PERIPH_CLK 45
|
||||||
|
|
||||||
// I2C SR1 Status Register #1 bit definitions for convenience
|
// I2C SR1 Status Register #1 bit definitions for convenience
|
||||||
// #define I2C_SR1_SMBALERT (1<<15) // SMBus alert
|
// #define I2C_SR1_SMBALERT (1<<15) // SMBus alert
|
||||||
|
@ -97,8 +105,6 @@ enum {TS_IDLE,TS_START,TS_W_ADDR,TS_W_DATA,TS_W_STOP,TS_R_ADDR,TS_R_DATA,TS_R_ST
|
||||||
* that it is only called at the beginning of an I2C transaction.
|
* that it is only called at the beginning of an I2C transaction.
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
void I2CManagerClass::I2C_setClock(uint32_t i2cClockSpeed) {
|
void I2CManagerClass::I2C_setClock(uint32_t i2cClockSpeed) {
|
||||||
return;
|
|
||||||
|
|
||||||
// Calculate a rise time appropriate to the requested bus speed
|
// Calculate a rise time appropriate to the requested bus speed
|
||||||
// Use 10x the rise time spec to enable integer divide of 50ns clock period
|
// Use 10x the rise time spec to enable integer divide of 50ns clock period
|
||||||
uint16_t t_rise;
|
uint16_t t_rise;
|
||||||
|
@ -106,13 +112,9 @@ void I2CManagerClass::I2C_setClock(uint32_t i2cClockSpeed) {
|
||||||
|
|
||||||
while (s->CR1 & I2C_CR1_STOP); // Prevents lockup by guarding further
|
while (s->CR1 & I2C_CR1_STOP); // Prevents lockup by guarding further
|
||||||
// writes to CR1 while STOP is being executed!
|
// writes to CR1 while STOP is being executed!
|
||||||
|
|
||||||
// Disable the I2C device, as TRISE can only be programmed whilst disabled
|
// Disable the I2C device, as TRISE can only be programmed whilst disabled
|
||||||
s->CR1 &= ~(I2C_CR1_PE); // Disable I2C
|
s->CR1 &= ~(I2C_CR1_PE); // Disable I2C
|
||||||
// Software reset the I2C peripheral
|
|
||||||
// s->CR1 |= I2C_CR1_SWRST; // reset the I2C
|
|
||||||
// delay(1);
|
|
||||||
// Release reset
|
|
||||||
// s->CR1 &= ~(I2C_CR1_SWRST); // Normal operation
|
|
||||||
|
|
||||||
if (i2cClockSpeed > 100000L)
|
if (i2cClockSpeed > 100000L)
|
||||||
{
|
{
|
||||||
|
@ -127,19 +129,20 @@ void I2CManagerClass::I2C_setClock(uint32_t i2cClockSpeed) {
|
||||||
t_rise = 1000; // nanoseconds
|
t_rise = 1000; // nanoseconds
|
||||||
}
|
}
|
||||||
// Configure the rise time register
|
// Configure the rise time register
|
||||||
s->TRISE = t_rise * I2C_PERIPH_CLK / 1000UL + 1;
|
s->TRISE = (t_rise / (1000 / i2c_MHz)) + 1;
|
||||||
|
|
||||||
// Bit 15: I2C Master mode, 0=standard, 1=Fast Mode
|
// Bit 15: I2C Master mode, 0=standard, 1=Fast Mode
|
||||||
// Bit 14: Duty, fast mode duty cycle (use 2:1)
|
// Bit 14: Duty, fast mode duty cycle (use 2:1)
|
||||||
// Bit 11-0: FREQR = 16MHz => TPCLK1 = 62.5ns
|
// Bit 11-0: FREQR
|
||||||
if (i2cClockSpeed > 100000L) {
|
if (i2cClockSpeed > 100000L) {
|
||||||
// In fast mode, I2C period is 3 * CCR * TPCLK1.
|
// In fast mode, I2C period is 3 * CCR * TPCLK1.
|
||||||
ccr_freq = I2C_PERIPH_CLK * 1000000 / 3 / i2cClockSpeed;
|
//APB1clk1 / 3 / i2cClockSpeed = 38, but that results in 306KHz not 400!
|
||||||
s->CCR = (uint16_t)ccr_freq | 0x8000; // We need Fast Mode set
|
ccr_freq = 30; // So 30 gives 396KHz or so!
|
||||||
|
s->CCR = (uint16_t)(ccr_freq | 0x8000); // We need Fast Mode set
|
||||||
} else {
|
} else {
|
||||||
// In standard mode, I2C period is 2 * CCR * TPCLK1.
|
// In standard mode, I2C period is 2 * CCR * TPCLK1
|
||||||
ccr_freq = I2C_PERIPH_CLK * 1000000 / 2 / i2cClockSpeed;
|
ccr_freq = (APB1clk1 / 2 / i2cClockSpeed); // Should be 225 for 45Mhz APB1 clock
|
||||||
s->CCR = (uint16_t)ccr_freq;
|
s->CCR |= (uint16_t)ccr_freq;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable the I2C master mode
|
// Enable the I2C master mode
|
||||||
|
@ -151,7 +154,10 @@ void I2CManagerClass::I2C_setClock(uint32_t i2cClockSpeed) {
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
void I2CManagerClass::I2C_init()
|
void I2CManagerClass::I2C_init()
|
||||||
{
|
{
|
||||||
// Setting up the clocks
|
// Query the clockspeed from the STM32 HAL layer
|
||||||
|
APB1clk1 = HAL_RCC_GetPCLK1Freq();
|
||||||
|
i2c_MHz = APB1clk1 / 1000000UL;
|
||||||
|
// Enable clocks
|
||||||
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;//(1 << 21); // Enable I2C CLOCK
|
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;//(1 << 21); // Enable I2C CLOCK
|
||||||
// Reset the I2C1 peripheral to initial state
|
// Reset the I2C1 peripheral to initial state
|
||||||
RCC->APB1RSTR |= RCC_APB1RSTR_I2C1RST;
|
RCC->APB1RSTR |= RCC_APB1RSTR_I2C1RST;
|
||||||
|
@ -181,7 +187,8 @@ void I2CManagerClass::I2C_init()
|
||||||
s->CR2 &= 0xE000;
|
s->CR2 &= 0xE000;
|
||||||
|
|
||||||
// Set I2C peripheral clock frequency
|
// Set I2C peripheral clock frequency
|
||||||
s->CR2 |= I2C_PERIPH_CLK;
|
// s->CR2 |= I2C_PERIPH_CLK;
|
||||||
|
s->CR2 |= i2c_MHz;
|
||||||
|
|
||||||
// set own address to 00 - not used in master mode
|
// set own address to 00 - not used in master mode
|
||||||
I2C1->OAR1 = (1 << 14); // bit 14 should be kept at 1 according to the datasheet
|
I2C1->OAR1 = (1 << 14); // bit 14 should be kept at 1 according to the datasheet
|
||||||
|
@ -210,10 +217,14 @@ void I2CManagerClass::I2C_init()
|
||||||
// Bit 15: I2C Master mode, 0=standard, 1=Fast Mode
|
// Bit 15: I2C Master mode, 0=standard, 1=Fast Mode
|
||||||
// Bit 14: Duty, fast mode duty cycle
|
// Bit 14: Duty, fast mode duty cycle
|
||||||
// Bit 11-0: so CCR divisor would be clk / 2 / 100000 (where clk is in Hz)
|
// Bit 11-0: so CCR divisor would be clk / 2 / 100000 (where clk is in Hz)
|
||||||
s->CCR = I2C_PERIPH_CLK * 5;
|
// s->CCR = I2C_PERIPH_CLK * 5;
|
||||||
|
s->CCR &= ~(0x3000); // Clear all bits except 12 and 13 which must remain per reset value
|
||||||
|
s->CCR |= (APB1clk1 / 2 / 100000UL); // i2c_MHz * 5;
|
||||||
|
// s->CCR = i2c_MHz * 5;
|
||||||
|
|
||||||
// Configure the rise time register - max allowed is 1000ns, so value = 1000ns * I2C_PERIPH_CLK MHz / 1000 + 1.
|
// Configure the rise time register - max allowed is 1000ns, so value = 1000ns * I2C_PERIPH_CLK MHz / 1000 + 1.
|
||||||
s->TRISE = I2C_PERIPH_CLK + 1; // 1000 ns / 50 ns = 20 + 1 = 21
|
// s->TRISE = I2C_PERIPH_CLK + 1; // 1000 ns / 50 ns = 20 + 1 = 21
|
||||||
|
s->TRISE = i2c_MHz + 1;
|
||||||
|
|
||||||
// Enable the I2C master mode
|
// Enable the I2C master mode
|
||||||
s->CR1 |= I2C_CR1_PE; // Enable I2C
|
s->CR1 |= I2C_CR1_PE; // Enable I2C
|
||||||
|
|
Loading…
Reference in New Issue
Block a user