//Dactyl project v1.0//I2C interrupt code using the ST Perif library#include "i2c_int.h"#include "gpio.h"#include "Sensors/bmp085.h"#include "Sensors/pitot.h"#include "Sensors/accel_down.h"#include "Control/imu.h"volatile uint32_t Jobs,Completed_Jobs; //used for task control (only ever access this from outside for polling Jobs/Reading Completed_Jobs)volatile uint8_t job; //stores the current jobvolatile I2C_Error_Type I2C1error; //stores current error status//Setup the const jobs descriptorsconst uint8_t Magno_config[]=MAGNO_SETUP;const uint8_t Magno_single[]=MAGNO_SINGLE;const uint8_t Bmp_temperature[]={BMP085_TEMP};const uint8_t Bmp_pressure[]={BMP085_PRES};const uint8_t Accel_config[]=ACCEL_SETUP;const uint8_t Gyro_config[]=GYRO_SETUP;const uint8_t Gyro_clk_config[]=ITG_CLOCK;const uint8_t Pitot_conv[]={LTC2481_ADC};volatile I2C_Job_Type I2C_jobs[]=I2C_JOBS_INITIALISER;//sets up the const jobs/** * @brief This function handles I2C1 Event interrupt request. * @param : None * @retval : None */void I2C1_EV_IRQHandler(void) { static uint8_t subaddress_sent,final_stop;//flag to indicate if subaddess sent, flag to indicate final bus condition static int8_t index; //index is signed -1==send the subaddress if(!((Jobs>>job)&0x00000001)) //if the current job bit is not set for(job=0;!((Jobs>>job)&0x00000001) && job<I2C_NUMBER_JOBS;job++);//find the first uncompleted job, starting at current job zero if(I2C_GetITStatus(I2C1,I2C_IT_SB)) {//we just sent a start - EV5 in ref manual I2C_AcknowledgeConfig(I2C1, ENABLE);//make sure ACK is on index=0; //reset the index if(I2C_Direction_Receiver==I2C_jobs[job].direction && (subaddress_sent || 0xFF==I2C_jobs[job].subaddress)) {//we have sent the subaddr subaddress_sent=1;//make sure this is set in case of no subaddress, so following code runs correctly I2C_Send7bitAddress(I2C1,I2C_jobs[job].address,I2C_Direction_Receiver);//send the address and set hardware mode } else { //direction is Tx, or we havent sent the sub and rep start I2C_Send7bitAddress(I2C1,I2C_jobs[job].address,I2C_Direction_Transmitter);//send the address and set hardware mode if(0xFF!=I2C_jobs[job].subaddress)//0xFF as subaddress means it will be ignored, in Tx or Rx mode index=-1;//send a subaddress } } else if(I2C_GetITStatus(I2C1,I2C_IT_ADDR)) {//we just sent the address - EV6 in ref manual volatile uint16_t a=I2C1->SR1;asm volatile("dmb");//Read SR1,2 to clear ADDR if(1==I2C_jobs[job].bytes && I2C_Direction_Receiver==I2C_jobs[job].direction && subaddress_sent) {//we are receiving 1 byte - EV6_3 I2C_AcknowledgeConfig(I2C1, DISABLE);//turn off ACK a=I2C1->SR2;asm volatile("dmb"); if(Jobs&~(1<<job)) {//check if there are other jobs requested other than the current one final_stop=0;//we finish with a repeated start I2C_GenerateSTART(I2C1,ENABLE);//program the repeated start } else { final_stop=1;//finish with a stop if there are no more jobs I2C_GenerateSTOP(I2C1,ENABLE);//program the stop } I2C_ITConfig(I2C1, I2C_IT_BUF, ENABLE);//allow us to have an EV7 } else { a=I2C1->SR2;asm volatile("dmb"); //EV6 and EV6_1 if(2==I2C_jobs[job].bytes && I2C_Direction_Receiver==I2C_jobs[job].direction && subaddress_sent) { //rx 2 bytes - EV6_1 for(volatile uint8_t b=0x2F;b;b--);//very short delay to let the hardware start up //I2C_AcknowledgeConfig(I2C1, DISABLE);//turn off ACK I2C_ITConfig(I2C1, I2C_IT_BUF, DISABLE);//disable TXE to allow the buffer to fill } else if(3==I2C_jobs[job].bytes && I2C_Direction_Receiver==I2C_jobs[job].direction && subaddress_sent)//rx 3 bytes I2C_ITConfig(I2C1, I2C_IT_BUF, DISABLE);//make sure RXNE disabled so we get a BTF in two bytes time else //receiving greater than three bytes, sending subaddress, or transmitting I2C_ITConfig(I2C1, I2C_IT_BUF, ENABLE); } } else if(I2C_GetITStatus(I2C1,I2C_IT_BTF)) {//Byte transfer finished - EV7_2, EV7_3 or EV8_2 if(Jobs&~(1<<job))//check if there are other jobs requested other than the current one final_stop=0;//we finish with a repeated start else final_stop=1;//finish with a stop if there are no more jobs if(I2C_Direction_Receiver==I2C_jobs[job].direction && subaddress_sent) {//EV7_2, EV7_3 if(I2C_jobs[job].bytes>2) {//EV7_2 I2C_AcknowledgeConfig(I2C1, DISABLE);//turn off ACK I2C_jobs[job].data_pointer[index++]=I2C_ReceiveData(I2C1);//read data N-2 if(final_stop) I2C_GenerateSTOP(I2C1,ENABLE);//program the Stop else I2C_GenerateSTART(I2C1,ENABLE);//send a repeated start I2C_jobs[job].data_pointer[index++]=I2C_ReceiveData(I2C1);//read data N-1 I2C_ITConfig(I2C1, I2C_IT_BUF, ENABLE);//enable RXNE to allow the final EV7 } else { //EV7_3 if(final_stop) I2C_GenerateSTOP(I2C1,ENABLE);//program the Stop else I2C_GenerateSTART(I2C1,ENABLE);//send a repeated start I2C_jobs[job].data_pointer[index++]=I2C_ReceiveData(I2C1);//read data N-1 I2C_jobs[job].data_pointer[index++]=I2C_ReceiveData(I2C1);//read data N index++;//to show job completed } } else {//EV8_2, which may be due to a subaddress sent or a write completion if(subaddress_sent || (I2C_Direction_Transmitter==I2C_jobs[job].direction)) { if(final_stop) I2C_GenerateSTOP(I2C1,ENABLE);//program the Stop else I2C_GenerateSTART(I2C1,ENABLE);//send a repeated start index++;//to show that the job is complete } else { //We need to send a subaddress I2C_GenerateSTART(I2C1,ENABLE);//program the repeated Start subaddress_sent=1;//this is set back to zero upon completion of the current task } } } else if(I2C_GetITStatus(I2C1,I2C_IT_RXNE)) {//Byte received - EV7 I2C_jobs[job].data_pointer[index++]=I2C_ReceiveData(I2C1); if(I2C_jobs[job].bytes==(index+3)) I2C_ITConfig(I2C1, I2C_IT_BUF, DISABLE);//disable TXE to allow the buffer to flush so we can get an EV7_2 if(I2C_jobs[job].bytes==index)//We have completed a final EV7 index++; //to show job is complete } else if(I2C_GetITStatus(I2C1,I2C_IT_TXE)) {//Byte transmitted -EV8/EV8_1 if(-1!=index) { //we dont have a subaddress to send I2C_SendData(I2C1,I2C_jobs[job].data_pointer[index++]); if(I2C_jobs[job].bytes==index)//we have sent all the data I2C_ITConfig(I2C1, I2C_IT_BUF, DISABLE);//disable TXE to allow the buffer to flush } else { index++; I2C_SendData(I2C1,I2C_jobs[job].subaddress);//send the subaddress if(I2C_Direction_Receiver==I2C_jobs[job].direction || !I2C_jobs[job].bytes)//if receiving or sending 0 bytes, flush now I2C_ITConfig(I2C1, I2C_IT_BUF, DISABLE);//disable TXE to allow the buffer to flush } } if(index>=0 && (I2C_jobs[job].bytes+1)==(uint8_t)index) {//we have completed the current job //Completion Tasks go here if(GYRO_READ==job) { //if we completed the first task (read the gyro) NVIC_SetPendingIRQ(KALMAN_SW_ISR_NO);//set the kalman filter isr to run (in a lower pre-emption priority) /*if(MAG_DATA_READY&Get_MEMS_DRDY()) {//If magno data ready pin set (should be set in 1/160seconds, this is error handler) //I2C1_Request_Job(MAGNO_SETUP_NO);//setup the magno for new single sample I2C1_Request_Job(MAGNO_READ);//read the magno }*/ } else if(ACCEL_READ==job)//if we finished running the accel, run the accel downsampling function Accel_Downconvert();//Accelerometer downconversion function called, this reads the global readbytes array //End of completion tasks Jobs&=~(0x00000001<<job);//tick off current job as complete Completed_Jobs|=(0x00000001<<job);//These can be polled by other tasks to see if a job has been completed or is scheduled subaddress_sent=0; //reset this here if(Jobs && final_stop) {//there are still jobs left but we completed with a stop (i.e. a job request was made whilst last byte sending) while(I2C1->CR1&0x0200);//doesnt seem to be a better way to do this, must wait for stop to clear I2C_GenerateSTART(I2C1,ENABLE);//program the Start to kick start the new transfer } }}/** * @brief This function handles I2C1 Error interrupt request. * @param None * @retval : None * Note: The error and event handlers must be in the same priority group. Other interrupts may be in the group, but must be lower priority * ER must have the highest priority in the group (but not necessarily on the device - Method2 from ref manual) */void I2C1_ER_IRQHandler(void) { __IO uint32_t SR1Register, SR2Register; /* Read the I2C1 status register */ SR1Register = I2C1->SR1; if(SR1Register & 0x0F00) { //an error I2C1error.error=((SR1Register&0x0F00)>>8);//save error I2C1error.job=job; //the task } /* If AF, BERR or ARLO, abandon the current job and commence new if there are jobs*/ if(SR1Register & 0x0700) { SR2Register = I2C1->SR2;//read second status register to clear ADDR if it is set (note that BTF will not be set after a NACK) I2C_ITConfig(I2C1, I2C_IT_BUF, DISABLE);//disable the RXNE/TXE interrupt - prevent the ISR tailchaining onto the ER (hopefully) Jobs&=~(0x00000001<<job);//cancel the current job - abandoned, if(Jobs && !(I2C1->CR1&0x0100)) {//ensure start of a new job if there are still jobs left while(I2C1->CR1&0x0200);//wait for any stop to finish sending I2C_GenerateSTART(I2C1,ENABLE);//sets a start } else if(!(SR1Register & 0x0200) && !(I2C1->CR1&0x0100)) {//if we dont have an ARLO error, ensure sending of a stop if(I2C1->CR1&0x0100) {//We are currently trying to send a start, this is very bad as start,stop will hang the peripheral while(I2C1->CR1&0x0100);//wait for any start to finish sending I2C_GenerateSTOP(I2C1,ENABLE);//send stop to finalise bus transaction while(I2C1->CR1&0x0200);//wait for stop to finish sending I2C_Config();//reset and configure the hardware } else I2C_GenerateSTOP(I2C1,ENABLE);//stop to free up the bus } } I2C1->SR1 &=~0x0F00; //reset all the error bits to clear the interrupt}/** * @brief This function sets a job as requested on I2C1 * @param : job number * @retval : None */void I2C1_Request_Job(uint8_t job_) { if(job_<32) { //sanity check if(!Jobs && !(I2C1->CR1&0x0100))//if we are restarting the driver, ensure sending a start while(I2C1->CR1&0x0200);//wait for any stop to finish sending I2C_GenerateSTART(I2C1,ENABLE);//send the start for the new job Jobs|=1<<job_; //set the job bit }}/** * @brief This function sets the data pointer on a job * @param : job number, pointer to data * @retval : None */void I2C1_Setup_Job(uint8_t job_, volatile uint8_t* data) { if(job_<I2C_NUMBER_JOBS) I2C_jobs[job_].data_pointer=data;}/** * @brief Configures the I2C1 interface * @param None * @retval None */void I2C_Config() { // Configure I2C1 for the sensor bus I2C_DeInit(I2C1); //Deinit and reset the I2C to avoid it locking up I2C_SoftwareResetCmd(I2C1, ENABLE);//Generate a reset signal to the peripheral I2C_SoftwareResetCmd(I2C1, DISABLE); I2C_InitTypeDef I2C_InitStructure; I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_OwnAddress1 = 0xAD;//0xAM --> ADAM I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress= I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_ClockSpeed = 400000; //Setup the pointers to the read data I2C1_Setup_Job(GYRO_READ, (volatile uint8_t*)Gyro_Data_Buffer);//Gyro data buffer I2C1_Setup_Job(ACCEL_READ, (volatile uint8_t*)Accel_Data_Buffer);//Accel data buffer I2C1_Setup_Job(MAGNO_READ, (volatile uint8_t*)Magno_Data_Buffer);//Accel data buffer I2C1_Setup_Job(BMP_16BIT, (volatile uint8_t*)&Bmp_Temp_Buffer);//BMP temperature buffer I2C1_Setup_Job(BMP_24BIT, (volatile uint8_t*)&Bmp_Press_Buffer);//BMP pressure buffer I2C1_Setup_Job(BMP_READ, (volatile uint8_t*)&Our_Sensorcal);//BMP calibration data I2C1_Setup_Job(PITOT_READ, (volatile uint8_t*)&Pitot_Pressure);//Pitot (LTC2481 adc) read //Enable the hardware I2C_ITConfig(I2C1, I2C_IT_EVT|I2C_IT_ERR, ENABLE);//Enable EVT and ERR interrupts I2C_Init( I2C1, &I2C_InitStructure ); I2C_Cmd( I2C1, ENABLE );}