Estoy escribiendo un firmware de nivel de registro para mi STM32F446 para usar el periférico I2C para hablar con un acelerómetro. La secuencia de eventos que necesito hacer es la siguiente
1) START
2) SLAVE ADDRESS+Write
3) SUBADDRESS (command byte to specify which register address on the peripheral i want to read from)
4) REPEAT START
5) SLAVE ADDRESS+Read
6) Collect data from slave
7) STOP
En un analizador lógico puedo ver que estoy haciendo correctamente los pasos 1-3, pero cuando necesito afirmar el inicio repetido, parece que no lo hago correctamente. Estoy configurando el bit de registro 8 (desde cero) I2C1- > CR1 con esta función:
void repeat_start_I2C1(){
//set start bit
I2C1->CR1 |= (0x1 << 0x8);
}
que parece funcionar para mi primer inicio pero no está generando el inicio repetido, porque no está generando el inicio repetido mi función de lectura (utilizada en el paso número 5) se bloquea en un bucle mientras espera el bit de registro de estado SB
para ir alto antes de continuar (SB va alto después de cada inicio / inicio de repetición y es necesario borrar antes de enviar la dirección de esclavo):
void address_read_I2C1(uint8_t addr){
//wait for SB bit to be set
while(!(I2C1->SR1 & 0x1));
//write slave address with LSB set for receive mode
I2C1->DR = (addr << 1) | 0x1;
}
Aquí hay una imagen de la transmisión completa a medida que se produce hasta el inicio repetido falso que se produce directamente después de 0x2D + ACK (el byte de comando de subdirección, es decir, paso 3).
Aquíhayunprimerplanodeliniciorepetidofalso
Comopuedeverenlamarcadeverificación+1us,laSCL(líneasuperior)ylaSDA(Líneainferior)bajanalmismotiempo(hastalaresolucióndelanalizadorlógico)enlugardeSDA->SCLcomonecesarioenunacondicióndeinicio(quesepuedevercorrectamenteenlaprimeraimagen.
HiceunapausaduranteladepuraciónmientrasestabaatascadoenelbuclewhileynotéqueelbitdeBTF
delEstadodeRegistro2%eraalto,loqueindicaquelatransmisiónhabíafinalizadoyestapuedeserlarazónporlaquenosepuedeafirmarqueelinicioescorrecto.Peronoentiendoelprocedimientoquedeboseguirparasolucionarloparaquepuedarepetirelinicio.
EDITAR:
unusuariosugirióquepodríaomitirunpasoparaborrarelbitdeestadodeladirecciónantesdeenviarunbytedecomando,peromeolvidédemostrarestafunción
//WritebytetoI2C1voidwrite_I2C1_byte(uint8_tcmd){uint8_ttmp;//waitforADDR=1thenclearbyreadingSR1andSR1while(!(I2C1->SR1&&0x02));tmp=I2C1->SR1;tmp=I2C1->SR2;//waitforTxE=1todenotedataregisterreadytoaccepttransmissiondatawhile(!(I2C1->SR1&&0x40));I2C1->DR=cmd;}
Estafunciónrealizaelpaso3enmilistadepasosborrandoADDRleyendolosregistrosSRyluegoenviandounbyteaDR.Compilosinoptimizacionesparaquetmpnosecortedelcódigopornoserutilizado.
EDIT2:
Unusuariopreguntóquéresistenciasdepull-upestánconfiguradasparaelgpioconectadoalperiférico,aquíestálafuncióndeinicioqueconfiguralasresistenciasdepull-upparatirardelaslíneasaltas(líneasespaciadasespaciadas):
inlinevoidinit_i2c1(){//setGPIOtocorrectpinmodesforI2C1//PB_8IC21_SCL//PB_9I2C1_SDA//enableGPIOBclock//RCC->AHB1ENR|=RCC_AHB1ENR_GPIOBEN;RCC->AHB1ENR|=1<<(1);//setGPIOBclockenablebit//enableI2C1Clock//RCC->APB1ENR|=RCC_APB1ENR_I2C1EN;RCC->APB1ENR|=1<<(21);//setI2C1clockenablebit//PB_8IC21_SCL//setthemodetoalternatefuntionGPIOB->MODER&=~(0b11<<(8*2));//clearbitfieldusing2bitmaskGPIOB->MODER|=0b10<<(8*2);//setbitfildsto0b10toenablealternatefunctionmode//setoutputmodetoopendrainsoitcanbepulledlowGPIOB->OTYPER|=(1<<(8));//setGPIOspeedGPIOB->OSPEEDR&=~(0b11<<(8*2));//clearGPIOB->OSPEEDR|=(0b10<<(8*2));//fastspeed-50MHz//******************************************************//SetPullupResistorsGPIOB->PUPDR&=~(0b11<<(2*2));//clearGPIOB->PUPDR|=(0b01<<(2*2));//pullup//******************************************************//SetAlternateFunctionI2C1usingAlternateFunctionRegisterHighGPIOB->AFR[1]&=~(0b1111<<(0*4));//clearfieldGPIOB->AFR[1]|=(0b0100<<(0*4));//setforalternatefunction4(I2C1)//PB_9I2C1_SDAGPIOB->MODER&=~(0b11<<(9*2));//clearbitfieldusing2bitmaskGPIOB->MODER|=0b10<<(9*2);//setbitfildsto0b10toenablealternatefunctionmode//setoutputmodetoopendrainsoitcanbepulledlowGPIOB->OTYPER|=(1<<(9));//setGPIOspeedGPIOB->OSPEEDR&=~(0b11<<(9*2));//clearGPIOB->OSPEEDR|=(0b10<<(9*2));//fastspeed-50MHz//******************************************************//SetPullupResistorsGPIOB->PUPDR&=~(0b11<<(2*2));//clearGPIOB->PUPDR|=(0b01<<(2*2));//pullup//******************************************************//SetAlternateFunctionI2C1usingAlternateFunctionRegisterHighGPIOB->AFR[1]&=~(0b1111<<(1*4));//clearfieldGPIOB->AFR[1]|=(0b0100<<(1*4));//setforalternatefunction4(I2C1)//setupI2C1//disableI2C1tosetupI2C1->CR1&=~(1<<0);//Peripheralenablebit//setclockfrequencyto100kHzI2C1->CR2&=~(0b111111);I2C1->CR2|=(0b001000);//setI2Cperipheralfrequencyto8lMHzI2C1->CCR&=~(1<15);//clearthe15thbittosetittoSmmode//CalculatetheClockControlBits//CCR[11:0]indecimal=((1/2)(1/targetFrequency))/(1/PeripheralFrequency)//itis1/2thetargetperiodbecausetheCRRdefinesThighorTlow(halftheperiod)//CCR=((1/2)(1/100E3))/(1/8E6)decimaltohex//CCR=0x28I2C1->CCR&=~(0b111111111111);//clearthelowest12bitsI2C1->CCR|=(0x28);//clearthelowest12bits//SetTRISE//TRiseisthemaximumrisetimedividedbytheperipheralclockperiod+1//TRISE=floor(Trise_max/Tpclk)+1//ForSmmodethemaxrisetimeis1000ns//thuswehave((1000E-9/(1/8E6))+1)=9I2C1->TRISE=0b001001;//setTRISEto9//ProgramtheI2C_CR1registertoenabletheperipheralI2C1->CR1|=(1<<0);//Peripheralenablebit}
EDIT3:
Deacuerdocon2.4.3 Mismatch on the “Setup time for a repeated Start condition” timing parameter
Aparentemente hay algunos casos en los que se viola el tiempo de configuración para el inicio repetido en I2C velocidades de modo estándar entre 88-100KHz (que actualmente estoy operando desde que estoy a 100KHz). El tiempo mínimo requerido para la configuración del inicio repetido es 4.7us, pero al mirar la salida de mi analizador lógico, tengo menos de 3us cuando SCL está alto y cuando la línea SDA parece intentar un inicio repetido. Aunque este problema no se supone que ocurra si el esclavo no está estirando el reloj y el tiempo de aumento de SCL es inferior a 300 ns, lo que mis imágenes indican, así que no estoy seguro de por qué se está infringiendo la configuración.
Intentaré reducir la frecuencia por debajo de 88KHz como solución sugerida en la Errata, y le informaré.
EDIT4: reducir SCL a menos de 80KHz no hizo nada, de hecho lo empeoró. Ahora, la primera condición de inicio tiene el mismo comportamiento (fallar hacia abajo exactamente al mismo tiempo que SDA). Pero esto solo ocurre de forma intermitente, a veces funciona y otras no ...
EDIT5: RESUELTO. Los pull-ups externos son muy necesarios ... estúpida placa del núcleo ...