[英]Am I setting I2C CCR and TRISE wrong? STM32 [bare-metal]
按照@wovano 的建議,我將把這個問題簡化。 對於舊版本的問題,請參閱編輯歷史。
我正在嘗試在我的項目( STM32F407ZGT6 , MMA8452Q加速度計)中包含 I2C,並且我正在設置所有裸機。 我將gschorcht 庫用於加速度計功能。 我目前無法使用示波器或 FT232H。
我不確定我的 I2C 是否正常工作:當我嘗試讀取我的設備 ID 時,我得到的是 0x22 而不是0x2A *。 我也無法循環讀取數據。 如果我使用if (mma845x_new_data (sensor) && mma845x_get_float_data (sensor, &data))
,我永遠不會得到積極的條件,並且如下調整會迫使我進入Default_Handler: Infinite_Loop進行調試:
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);
}
我想知道我的 CCR 或 TRISE 設置是否有問題(我包含在我正在使用的計算下方)。 我的SYSCLK
為 168MHz, APB1
為 42MHz( APB1PRESC
= 4),我目前正在測試標准模式。
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;
}
*這可能是因為它是一個“不同的品牌”。 盡管它引起了一些懷疑,即它沒有讀取所有位(二進制中的 0x2A 是0b0010 1010
和 0x22 是0b0010 0010
),但用另一個加速度計測試它會給我 0x05 的值。 有可能會丟失一些位,但我不確定。
你確定你的引腳配置正確嗎? 檢查 GPIOx MODER 寄存器。 如果它不起作用。 您可以查看本教程。 我也使用此代碼。 這里
我認為我的 I2C 通信確實有效! 所以答案是“是的,我的 CCR 和 TRISE”設置是正確的(至少對於 100kHz 模式)。 為了檢查這一點,我借了一個 Arduino 並粘貼了一個簡單的Wire
代碼來讀取和寫入線路。 有了它,我發現:
mAddr
時遇到問題 我錯誤地將包含內存地址而不是其內容的變量的地址發送給函數(而不是mAddr = 0x1D
,我將&mAddr
作為參數傳遞)。
此外,@wovano 關於 HardFault 的猜測也是正確的。 printf 導致崩潰,要么是因為浮點鏈接,要么是因為它正在讀取一個空變量(因為它沒有獲取任何數據)。
我仍然在從 MMA 讀取數據時遇到問題,因為我的STATUS->ZYXDR
標志永遠不會變高( mma845x_new_data(sensor)
總是返回 0)。 我的初始化如下,以防有人對飛思卡爾半導體有經驗(圖書館鏈接):
#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);
}
}
但是最初的問題已經解決了。 感謝您的見解。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.