我正在使用 KEIL。这是我的 I2C 设置代码。我正在尝试使用中断进行设置。实际上,一切都像这样正常工作。但我唯一的问题是我做了一件奇怪的事情才能让它发挥作用。
// Variables passed to I2C1_EV_IRQHandler
static volatile uint8_t counter=0;
static uint8_t slave_address=0, *data_buff,buff_num_bytes=0;
//I2c event and error interrupts setup
void I2C_NVIC_setup(void){
NVIC->ISER[0] |=NVIC_ISER_SETENA_31; //I2C_IRQ_EV is at 31 position
NVIC->ISER[1] |=NVIC_ISER_SETENA_0; //I2C_IRQ_ER is at 32 position
NVIC->IP[31] &= ~0xF0;
NVIC->IP[31]= (uint8_t)(0x5<<4); //Event has lower priority
NVIC->IP[32] &= ~0xF0;
NVIC->IP[32]= (uint8_t)(0x4<<4); //Error has higher priority
__enable_irq(); //ENABLE interrupts
}
//GPIO settings for PB6 and PB7
void I2C_GPIO_init(void){
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; //PORTB clock enable
GPIOB->CRL |= GPIO_CRL_CNF6 |GPIO_CRL_CNF7; //PB6 and PB7- open drain
uint32_t buff=GPIOB->CRL;
buff &= ~GPIO_CRL_MODE6 & ~GPIO_CRL_MODE7;
buff |= GPIO_CRL_MODE6_0 | GPIO_CRL_MODE7_0 ; //2MHz maximum output speed
GPIOB->CRL=buff;
//Dont care about ODR register
}
// Initialization of I2C
bool I2C_init(void){
I2C_GPIO_init();
I2C_NVIC_setup();
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
I2C1->CR2 &= ~I2C_CR2_FREQ;
I2C1->CR2 |= 0x14; //APB1 frequency is 20MHz
I2C1->CCR |= I2C_CCR_FS; //Fast mode
I2C1->CCR |= I2C_CCR_DUTY; //Duty cycle 16/9(for fast mode)
I2C1->CCR &= ~I2C_CCR_CCR;
I2C1->CCR |= 0x04; //Thigh=1800ns Tlow=3200ns -200KHz frequency
I2C1->TRISE &= ~I2C_TRISE_TRISE;
I2C1->TRISE |= 0x07; //300ns(max Trise)/50ns(Tclck) +1
I2C1->CR1 |= I2C_CR1_PE; //Enable peripheral
I2C1->CR2 |= I2C_CR2_ITBUFEN; //Enable interupts of TxE and RxNE
I2C1->CR2 |= I2C_CR2_ITEVTEN; //Enable event interupts
I2C1->CR2 |= I2C_CR2_ITERREN; //Enable error interupts
}
//Master transmitter
bool I2C_transmit(uint8_t b7_address, uint8_t* data, uint8_t num_bytes){
counter=num_bytes;
buff_num_bytes=num_bytes;
data_buff=data;
slave_address=b7_address<<1 ;//To enter transmiter mode LSB=0
I2C1->CR1 |= I2C_CR1_START; //Start condition
while(counter || (I2C1->SR2 & I2C_SR2_MSL) || (I2C1->CR1 & I2C_CR1_STOP)){ //Stop bit is checked because of the problem I have
}
}
//IRQ I2C1 event handler
void I2C1_EV_IRQHandler(void){ //Handle the interrupt of I2C1
uint8_t buff_sr1=I2C1->SR1;
if(buff_sr1 & I2C_SR1_SB){ //SB bit is set(unsetting by read SR1 and writing adress to DR)
I2C1->DR=slave_address;
}else if(buff_sr1 & I2C_SR1_ADDR){ //ADDR bit is set(unsetting by read SR1 and read SR2)
(void)I2C1->SR2;
}
if ((buff_sr1 & I2C_SR1_TXE)&& !(counter==0)){ //Checking TxE( clearing by writting to DR)
I2C1->DR=data_buff[buff_num_bytes-counter];
counter--;
} else if((buff_sr1 & I2C_SR1_TXE)&&(buff_sr1 & I2C_SR1_BTF)){ //Checking stop
//condition(TxE=1,BTF=1,counter=0)
(void)I2C1->SR1; //Dont know why, but it works(just read some I2C1 register)
I2C1->CR1 |= I2C_CR1_STOP; //Generate stop condition
}
}
如果你看一下最后几行:
} else if((buff_sr1 & I2C_SR1_TXE)&&(buff_sr1 & I2C_SR1_BTF)){ //Checking stop
//condition(TxE=1,BTF=1,counter=0)
(void)I2C1->SR1; //Dont know why, but it works(just read some I2C1 register)
I2C1->CR1 |= I2C_CR1_STOP; //Generate stop condition
}
这里当我调试时(这段代码,没有行(void)I2C1->SR1;),当我尝试发送数据时,第一个包将被发送而没有任何问题。最后它会产生停止条件(MSL 位将被清除,并且线路被释放),但 STOP 位将保持设置(这就是我在 I2C_transmit while 循环中放置 STOP 位检查的原因)。如果我在调试窗口中手动清除 STOP 位,它会继续,但在下一个发送周期它不会产生正确的启动(SB 将被设置,但 MSL 位将保持在复位状态)。
在调试时,如果我在此行之前放置断点:
(请记住,此时我没有找到解决这个问题的方法,所以没有这条线,一切都一样 - (void)I2C1->SR1;)
I2C1->CR1 |= I2C_CR1_STOP; //Generate stop condition
停在那里,检查寄存器,然后继续,一切正常。我在stackexchage上发现:
特别是关于读取寄存器的部分可能会给您带来调试地狱,因为调试器的读取将触发状态机,就像读取您的代码一样,所以一旦您停止并检查寄存器(有时调试器会自动完成所有操作)读取发生,外围设备将再次执行某些操作。
很明显,当我触发 STOP 条件时,有些要求没有得到满足。所以我决定检查是否是这种情况,似乎这是一个解决方案。添加读取 I2C 的任何寄存器((void)I2C1->SR1;//或任何其他 I2C 寄存器),解决了问题。必须有合乎逻辑的解释,但我找不到。我非常仔细地研究了参考手册,结果是这段代码,但我没有找到任何可以解释这一点的东西。
以下是参考手册中有关主传输的表格,供您参考:
你能说出我没有满足什么条件,所以我的 STOP 没有得到妥善处理。