Following @wovano's suggestion, I'll be boiling down the question. For the old versions of the question, see the editing history.
I'm trying to include I2C on my project (STM32F407ZGT6, MMA8452Q accelerometer) and I'm setting everything bare-metal. I'm using gschorcht library for the accelerometer functions. I currently do not have access to an osciloscope nor to an FT232H.
I am unsure if my I2C is working properly: when I try to read my Device ID, I get 0x22 instead of 0x2A*. I'm also not being able to read data in a loop. If I use if (mma845x_new_data (sensor) && mma845x_get_float_data (sensor, &data))
, I never get a positive condition, and adjusting as below forces me into the Default_Handler: Infinite_Loop for debugging:
void read_data(void)
{
mma845x_float_data_t data;
if (mma845x_new_data (sensor))
{
if(mma845x_get_float_data (sensor, &data))
{
// max. full scale is +-16 g and best resolution is 1 mg, i.e. 5 digits
printf("[MMA845X (xyz)[g]] ax=%+7.3f ay=%+7.3f az=%+7.3f\n", data.ax, data.ay, data.az);
return;
}
}
printf("[not new MMA845X (xyz)[g]] ax=%+7.3f ay=%+7.3f az=%+7.3f\n", data.ax, data.ay, data.az);
}
The Debug tab shows this trace/breakpoint trap thread:
I'm wondering if I did something wrong with my CCR or TRISE setting (I included below the calculations I'm using). I have my SYSCLK
at 168MHz and APB1
at 42MHz (APB1PRESC
= 4), and I'm currently testing the Standard Mode.
void init_gpio(void)
{
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; //Port B
GPIOB->MODER |= (2U << 12U); //MMA845_SCL || PB6
GPIOB->AFR[0] |= (4U << 24U); //AF4
GPIOB->OTYPER |= (1U << 6U); //Output Type to Open Drain
GPIOB->PUPDR |= (1U << 12U); //Set to Pull-Up
GPIOB->MODER |= (2U << 14U); //MMA845_SDA || PB7
GPIOB->AFR[0] |= (4U << 28U); //AF4
GPIOB->OTYPER |= (1U << 7U); //Output Type to Open Drain
GPIOB->PUPDR |= (1U << 14U); //Set to Pull-Up
}
/*i2c_init*/
void i2c_init(I2C_TypeDef *I2Cx)
{
/*Enable clock access to I2Cs*/
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; //enable I2C1
// RCC->APB1ENR |= RCC_APB1ENR_I2C2EN; //enable I2C2
// RCC->APB1ENR |= RCC_APB1ENR_I2C3EN; //enable I2C3
RCC->APB1ENR;
/*Reset I2C*/
I2Cx->CR1 |= I2C_CR1_SWRST; /*!BUSY ON */
I2Cx->CR1 &= ~I2C_CR1_SWRST; /*!BUSY OFF*/
int CCR_VAL;
int TRISE_VAL;
int MAX_FRQ;
/*Set I2C frequency same as APB*/
MAX_FRQ = (SYSCLK/APB1PRESC)/1000000; //= 42 @ max clock speed
I2Cx->CR2 &= ~msk(6,0); /*!BUSY ON*/
I2Cx->CR2 |= MAX_FRQ;
if(!MASTER_MODE){
/* Standard Mode = 100kb/s*/
/* CCR calculated by half-period of 100k
* divided by the period of the PCLK
* CCR = ((1/100'000)/2)/(1/42'000'000)
* CCR = 5000ns/~24ns = 210
*/
CCR_VAL = 210;
/* TRISE calculated by PCLK (in MHz) + 1
* TRISE = ((42'000'000/1'000'000) + 1)
* TRISE = 42 + 1 = 43
*/
TRISE_VAL = 43;
}
else
{
/*Fast Mode = 400kb/s*/
if(DUTY_MODE)
{
/*16:9 ratio*/
/* CCR calculated by half-period of 100k
* divided by the sum of the duty ratios
* divided by the period of the PCLK
* t_high = 9 * CCR * TPCLK
* t_low = 16 * CCR * TPCLK
* t_high + t_low = 25 * CCR * TPCLK
* CCR = ((1/400'000)/2) / (25/42'000'000)
* CCR = 1250ns/~595ns = 2.1 ~ 2
*/
CCR_VAL = 2;
}
else
{
//2:1 ratio
/* CCR calculated by half-period of 100k
* divided by the sum of the duty ratios
* divided by the period of the PCLK
* t_high = CCR * TPCLK
* t_low = 2 * CCR * TPCLK
* t_high + t_low = 3 * CCR * TPCLK
* CCR = ((1/400'000)/2) / (3/42'000'000)
* CCR = 1250ns/~71ns = 17.5 ~ 17
*/
CCR_VAL = 17;
}
/* TRISE calculated by PCLK (in MHz) * 0.3ms + 1
* TRISE = ((42'000'000/1'000'000)*(300/1000) + 1)
* TRISE = 42 * 0.3 + 1 = 13.6 ~ 14
*/
TRISE_VAL = 14;
}
/*Set Clock Control value*/
I2Cx->CCR |= CCR_VAL; //assuming APB1 = 42MHz, SM = True
I2Cx->CCR |= (set(DUTY_MODE,I2C_CCR_DUTY_Pos) | set(MASTER_MODE,I2C_CCR_FS_Pos));
I2Cx->TRISE |= TRISE_VAL; //assuming APB1 = 42MHz, SM = True
/*Enable I2C*/
I2Cx->CR1 |= I2C_CR1_PE;
}
void set_addr(I2C_TypeDef *I2Cx, uint8_t sAddr, const uint8_t *mAddr, int read)
{
volatile int temp;
/*Check I2C is not busy*/
while(I2Cx->SR2 & I2C_SR2_BUSY);
/*(1) Generate a START, wait for start flag*/
I2Cx->CR1 |= I2C_CR1_START;
while (!(I2Cx->SR1 & I2C_SR1_SB));
/*Transmit slave address + 'write'*/
I2Cx->DR = sAddr << 1;
/*Wait for the address flag*/
while (!(I2Cx->SR1 & I2C_SR1_ADDR));
temp = I2Cx->SR2; //Clear address flag
/*Send memory address*/
//Make sure TXE is emtpy
while (!(I2Cx->SR1 & I2C_SR1_TXE));
I2Cx->DR = mAddr;
//Wait until transmitter is emtpy
while (!(I2Cx->SR1 & I2C_SR1_TXE));
if(read)
{
/*(2) Generate a START, wait for start flag*/
I2Cx->CR1 |= I2C_CR1_START;
while (!(I2Cx->SR1 & I2C_SR1_SB));
/*Transmit slave address + 'read'*/
I2Cx->DR = (sAddr << 1) | 1;
/*Wait for the address flag*/
while (!(I2Cx->SR1 & I2C_SR1_ADDR));
temp = I2Cx->SR2; //Clear address flag
}
}
int i2c_burst_read(I2C_TypeDef *I2Cx, uint8_t sAddr, const uint8_t *mAddr, uint8_t *string, uint32_t size)
{
if(size == 0)
{
return 0;
}
uint8_t *pData = string; //points at the first byte (char) of string
set_addr(I2Cx, sAddr, mAddr, 1);
/*Enable ACK*/
I2Cx->CR1 |= I2C_CR1_ACK;
while(size > 0U)
{
if(size-- == 1)
{
/*Disable ACK*/
I2Cx->CR1 &= ~I2C_CR1_ACK;
/*Wait until the receive flag is set*/
while(!(I2Cx->SR1 & I2C_SR1_RXNE));
//"Generate STOP" was here before
/*Read data from the DR*/
*pData++ = I2Cx->DR;
break;
}
else
{
/*Wait until the receive flag is set*/
while(!(I2Cx->SR1 & I2C_SR1_RXNE));
/*Read data from the DR*/
*pData++ = I2Cx->DR;
}
}
/*Generate a STOP after receiving*/
I2Cx->CR1 |= I2C_CR1_STOP;
return 1;
}
int i2c_burst_write(I2C_TypeDef *I2Cx, uint8_t sAddr, const uint8_t *mAddr, uint8_t *string, uint32_t size)
{
uint8_t *pData = string; //points at the first byte (char) of string
set_addr(I2Cx, sAddr, mAddr, 0);
while(size > 0U)
{
/*Wait for transmitter to be empty*/
while(!(I2Cx->SR1 & I2C_SR1_TXE));
I2Cx->DR = *pData++;
size--;
}
/*Wait for byte transfer finished flag*/
while(!(I2Cx->SR1 & I2C_SR1_BTF));
/*Generate a STOP after writing*/
I2Cx->CR1 |= I2C_CR1_STOP;
return 1;
}
*This might be due to it being a "different brand". Although it raises some suspicion that it isn't reading all the bits (0x2A in binary is 0b0010 1010
and 0x22 is 0b0010 0010
), testing it with another accelerometer gives me the value of 0x05. There's a chance bits are being lost but I'm not sure.
I figured my I2C communication was actually working! So the answer is 'Yes, my CCR and TRISE' settings were correct (at least for the 100kHz mode). To check this, I borrowed an Arduino and I pastade a simple Wire
code to read and write from the line. With it, I discovered that:
mAddr
I was mistakenly sending to the function the address of the variable containing the memory address instead of its content (instead of mAddr = 0x1D
, I was passing &mAddr
as argument).
Also, @wovano guess about the HardFault was correct too. The printf was causing the crash, either because of the floating-point linkage or because it was reading an empty variable (since it wasn't getting any data).
I'm still having issues reading data from the MMA since my STATUS->ZYXDR
flag never goes high (mma845x_new_data(sensor)
always returns 0). My initialization is as follows, in case anyone has experience with Freescale semiconductors (library link):
#include "mma845x.h"
#define MMA845X_I2C_ADDRESS_1 0x1c // SDO pin is low
#define MMA845X_I2C_ADDRESS_2 0x1d // SDO pin is high
static mma845x_sensor_t* sensor;
void init_MMA_sensor(void)
{
sensor = mma845x_init_sensor(I2C1, MMA845X_I2C_ADDRESS_2);
mma845x_config_int_signals(sensor, mma845x_high_active, mma845x_push_pull);
mma845x_config_hpf(sensor, 0, true);
mma845x_set_scale(sensor, mma845x_scale_2_g);
mma845x_set_mode(sensor, mma845x_high_res, mma845x_odr_50, true, false); //make it active!
}
void read_data(void)
{
mma845x_float_data_t data;
if (mma845x_new_data(sensor))
{
if(mma845x_get_float_data(sensor, &data))
{
// max. full scale is +-16 g and best resolution is 1 mg, i.e. 5 digits
printf("[MMA845X (xyz)[g]] ax=%+7.3f ay=%+7.3f az=%+7.3f\r\n",
data.ax, data.ay, data.az);
return;
}
}
printf("No new data available\r\n");
}
int main(void)
{
/*Setup*/
clock_init_168();
init_systick_MS(SYSTICK_LOAD_VAL_MS);
gpio_init();
uart_init();
i2c_init(I2C1);
init_MMA_sensor();
for(;;)
{
read_data();
delayMS(500);
}
}
But the intial question is resolved. Thanks for all your insight.