All pastes #2104094 Raw Edit

Untitled

public text v1 · immutable
#2104094 ·published 2012-01-19 16:06 UTC
rendered paste body
//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 job
volatile I2C_Error_Type I2C1error;	//stores current error status

//Setup the const jobs descriptors
const 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
		subaddress_sent=0;
	}
	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 - Seems to need a stop to fix hardware
				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
				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
					final_stop=1;
				//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
					final_stop=1;
				//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
			}
		}
		while(I2C1->CR1&0x0300){;}//we must wait for the start or stop to clear, otherwise we get constant BTF
	}
	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&0x0200)) {//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_16_9;
	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 );
}