Очередная попытка реализовать чтение IMU как в рабочей прошивке оказалась провальной. Поэтому было принято решение перенести проект на C++ и писать его подобно рабочей прошивке. Реализован драйвер для I2C. Добавлены файлы интерфейса IMU и конкретного ICM20948.
282 lines
5.8 KiB
C
282 lines
5.8 KiB
C
#include "imu.h"
|
||
|
||
/*static I2C_Request* current_req = 0;
|
||
static uint8_t i2c_buf[16];
|
||
static uint8_t i2c_index = 0;*/
|
||
|
||
static I2C_Request* i2c_head = 0;
|
||
static I2C_Request* i2c_current = 0;
|
||
|
||
static uint8_t imu_buffer[16];
|
||
|
||
void imu_pow_init()
|
||
{
|
||
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOCEN;
|
||
GPIOC->MODER &= ~(3 << (13 * 2));
|
||
GPIOC->MODER |= 1 << (13 * 2);
|
||
GPIOC->OTYPER &= ~(1 << 13);
|
||
GPIOC->PUPDR &= ~(3 << (13 * 2));
|
||
GPIOC->BSRR = 1 << (13 + 16);
|
||
}
|
||
|
||
void i2c_gpio_init()
|
||
{
|
||
// enable GPIOB clock
|
||
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOBEN;
|
||
|
||
// Alt function mode PB8, PB9
|
||
GPIOB->MODER &= ~((3 << 8 * 2) | (3 << 9 * 2));
|
||
GPIOB->MODER |= (2 << (8 * 2)) | (2 << (9 * 2));
|
||
|
||
// select AF4 for I2C1 at PB8 and PB9
|
||
GPIOB->AFR[1] &= ~((0xF << ((8 - 8) * 4)) | (0xF << ((9 - 8) * 4)));
|
||
GPIOB->AFR[1] |= ((4 << ((8 - 8) * 4)) | (4 << ((9 - 8) * 4)));
|
||
|
||
// high speed
|
||
GPIOB->OSPEEDR |= (2 << (8 * 2)) | (2 << (9 * 2));
|
||
|
||
// enable open-drain
|
||
GPIOB->OTYPER |= (1 << 8) | (1 << 9);
|
||
|
||
// set pull-up
|
||
GPIOB->PUPDR &= ~((3 << (8 * 2)) | (3 << (9 * 2)));
|
||
GPIOB->PUPDR |= (1 << 8 * 2) | (1 << 9 * 2);
|
||
}
|
||
|
||
void i2c1_init()
|
||
{
|
||
|
||
RCC->APB1ENR1 |= RCC_APB1ENR1_I2C1EN; // enable I2C1
|
||
|
||
I2C1->TIMINGR = 0x10802D9BUL; // 400 kHz @ 16 MHz
|
||
|
||
I2C1->CR1 |= I2C_CR1_PE;
|
||
}
|
||
|
||
void imu_init()
|
||
{
|
||
// select bank 0
|
||
i2c_write(ICM_ADDR, REG_BANK_SEL, ~(3 << 4));
|
||
|
||
// wake up, auto clock
|
||
i2c_write(ICM_ADDR, REG_PWR_MGMT_1, 1);
|
||
|
||
// select bank 2
|
||
i2c_write(ICM_ADDR, REG_BANK_SEL, 2 << 4);
|
||
|
||
// gyro ~2000 dps, FS_SEL = 3
|
||
i2c_write(ICM_ADDR, REG_GYRO_CONFIG_1, GYRO_FS_SEL_2000 | GYRO_DLPFCFG_73 | GYRO_FCHOICE_ON);
|
||
|
||
// accel 8g, FS_SEL = 2
|
||
i2c_write(ICM_ADDR, REG_ACCEL_CONFIG, ACCEL_FS_SEL_8 | ACCEL_DLPFCFG_69 | ACCEL_FCHOICE_ON);
|
||
|
||
// back to bank 0
|
||
i2c_write(ICM_ADDR, REG_BANK_SEL, ~(3 << 4));
|
||
}
|
||
|
||
void imu_tim6_init(const uint16_t freq)
|
||
{
|
||
RCC->APB1ENR1 |= RCC_APB1ENR1_TIM6EN;
|
||
|
||
TIM6->CR1 = 0;
|
||
TIM6->ARR = 1000 - 1;
|
||
TIM6->PSC = (SystemCoreClock / 1000 / freq) - 1;
|
||
|
||
TIM6->DIER |= TIM_DIER_UIE; // interrupt enable
|
||
TIM6->CR1 |= TIM_CR1_CEN; // counter enable
|
||
|
||
NVIC_EnableIRQ(TIM6_DAC_IRQn);
|
||
}
|
||
|
||
void i2c_read(uint8_t addr, uint8_t reg, uint8_t* buf, uint8_t len)
|
||
{
|
||
i2c_wait_idle(I2C1);
|
||
|
||
// write register address
|
||
I2C1->CR2 = (addr << 1) | (1 << I2C_CR2_NBYTES_Pos) | I2C_CR2_START;
|
||
|
||
while (!(I2C1->ISR & I2C_ISR_TXIS));
|
||
I2C1->TXDR = reg;
|
||
|
||
while (!(I2C1->ISR & I2C_ISR_TC));
|
||
|
||
I2C1->CR2 = (addr << 1) | I2C_CR2_RD_WRN | (len << I2C_CR2_NBYTES_Pos) | I2C_CR2_AUTOEND | I2C_CR2_START;
|
||
|
||
for (uint8_t i = 0; i < len; ++i)
|
||
{
|
||
while (!(I2C1->ISR & I2C_ISR_RXNE));
|
||
buf[i] = I2C1->RXDR;
|
||
}
|
||
|
||
while (!(I2C1->ISR & I2C_ISR_STOPF));
|
||
I2C1->ICR |= I2C_ICR_STOPCF;
|
||
}
|
||
|
||
static void i2c_enqueue(I2C_Request* req)
|
||
{
|
||
__disable_irq();
|
||
|
||
req->Next = i2c_head;
|
||
i2c_head = req;
|
||
|
||
__enable_irq();
|
||
|
||
// если I2C свободен — запускаем
|
||
if (!i2c_busy)
|
||
{
|
||
NVIC_SetPendingIRQ(I2C1_EV_IRQn);
|
||
}
|
||
}
|
||
|
||
static void i2c_start_next()
|
||
{
|
||
if (!i2c_head)
|
||
{
|
||
i2c_busy = 0;
|
||
return;
|
||
}
|
||
|
||
i2c_busy = 1;
|
||
|
||
i2c_current = i2c_head;
|
||
i2c_head = i2c_head->Next;
|
||
|
||
I2C1->CR1 |= I2C_CR1_TXIE |
|
||
I2C_CR1_RXIE |
|
||
I2C_CR1_TCIE |
|
||
I2C_CR1_STOPIE;
|
||
|
||
NVIC_EnableIRQ(I2C1_EV_IRQn);
|
||
|
||
// старт записи регистра
|
||
I2C1->CR2 = (i2c_current->Address << 1) |
|
||
(1 << I2C_CR2_NBYTES_Pos) |
|
||
I2C_CR2_START;
|
||
}
|
||
|
||
void imu_get_async(void (*cb)(uint8_t* data, uint8_t size))
|
||
{
|
||
static I2C_Request req;
|
||
|
||
req.Callback = cb;
|
||
req.Buffer = imu_buffer;
|
||
req.Size = sizeof(imu_buffer);
|
||
|
||
req.Address = ICM_ADDR;
|
||
req.Write = 1;
|
||
req.Read = 12;
|
||
|
||
imu_buffer[0] = 0x2D; // регистр
|
||
|
||
i2c_enqueue(&req);
|
||
}
|
||
|
||
void i2c_write(uint8_t addr, uint8_t reg, uint8_t data)
|
||
{
|
||
i2c_wait_idle(I2C1);
|
||
|
||
// write register address
|
||
I2C1->CR2 = (addr << 1) | (2 << I2C_CR2_NBYTES_Pos) | I2C_CR2_START;
|
||
|
||
while (!(I2C1->ISR & I2C_ISR_TXIS));
|
||
I2C1->TXDR = reg;
|
||
|
||
while (!(I2C1->ISR & I2C_ISR_TXIS));
|
||
I2C1->TXDR = data;
|
||
|
||
while (!(I2C1->ISR & I2C_ISR_TC));
|
||
I2C1->CR2 |= I2C_CR2_STOP;
|
||
}
|
||
|
||
void I2C1_EV_IRQHandler()
|
||
{
|
||
static int test_irq = 0;
|
||
test_irq++;
|
||
|
||
|
||
uint32_t isr = I2C1->ISR;
|
||
|
||
if (!i2c_current)
|
||
{
|
||
i2c_start_next();
|
||
return;
|
||
}
|
||
|
||
static uint8_t index = 0;
|
||
|
||
// TXIS
|
||
if (isr & I2C_ISR_TXIS)
|
||
{
|
||
I2C1->TXDR = i2c_current->Buffer[0];
|
||
}
|
||
|
||
// TC → старт чтения
|
||
else if (isr & I2C_ISR_TC)
|
||
{
|
||
I2C1->CR2 = (i2c_current->Address << 1) |
|
||
I2C_CR2_RD_WRN |
|
||
(i2c_current->Read << I2C_CR2_NBYTES_Pos) |
|
||
I2C_CR2_AUTOEND |
|
||
I2C_CR2_START;
|
||
}
|
||
|
||
// RXNE
|
||
else if (isr & I2C_ISR_RXNE)
|
||
{
|
||
|
||
|
||
i2c_current->Buffer[index++] = I2C1->RXDR;
|
||
|
||
if (index >= i2c_current->Read)
|
||
index = 0;
|
||
}
|
||
|
||
// STOP
|
||
else if (isr & I2C_ISR_STOPF)
|
||
{
|
||
I2C1->ICR |= I2C_ICR_STOPCF;
|
||
|
||
if (i2c_current->Callback)
|
||
i2c_current->Callback(i2c_current->Buffer, i2c_current->Read);
|
||
|
||
i2c_current = 0;
|
||
|
||
i2c_start_next();
|
||
}
|
||
}
|
||
|
||
void imu_read_raw(imu_raw_t* data)
|
||
{
|
||
uint8_t buf[12];
|
||
|
||
i2c_read(ICM_ADDR, 0x2D, buf, 12);
|
||
|
||
data->ax = (buf[0] << 8) | buf[1];
|
||
data->ay = (buf[2] << 8) | buf[3];
|
||
data->az = (buf[4] << 8) | buf[5];
|
||
|
||
data->gx = (buf[6] << 8) | buf[7];
|
||
data->gy = (buf[8] << 8) | buf[9];
|
||
data->gz = (buf[10] << 8) | buf[11];
|
||
}
|
||
|
||
static void i2c_wait_idle(I2C_TypeDef* i2c)
|
||
{
|
||
int timeout = 100000;
|
||
while ((i2c->ISR & I2C_ISR_BUSY) && --timeout);
|
||
|
||
if (timeout == 0)
|
||
{
|
||
// сброс I2C
|
||
i2c->CR1 &= ~I2C_CR1_PE;
|
||
i2c->CR1 |= I2C_CR1_PE;
|
||
}
|
||
|
||
// while (i2c->ISR & I2C_ISR_BUSY);
|
||
|
||
i2c->ICR = I2C_ICR_STOPCF |
|
||
I2C_ICR_NACKCF |
|
||
I2C_ICR_BERRCF |
|
||
I2C_ICR_ARLOCF;
|
||
}
|