AnsweredAssumed Answered

USART Interrupts appear to kill I2C bus on F4

Question asked by dpeterson3 on Apr 20, 2013
Latest reply on Apr 21, 2013 by dpeterson3
I thought I was finally ready to test my code on my quad rotor. I have motors all spinning, serial comm working, i2c comm to sensor working. Then, I put the serial and i2c code together and fail. Whenever I have serial receive interrupts enabled, after a few transmissions to the device, my I2C code hangs, usually with an arbitration loss error flag raised (although sometimes with some other bus errors). I have serial interrupts disabled when I'm doing i2c reading, but it doesn't seem to matter. It seems that everything will truck along just fine as long as serial interrupts aren't enabled. That doesn't make sense to me since they are disabled when the error occurs, but just having them on at any point in the main loop breaks i2c. I need them or my control packets don't get through.

Here's the main loop with all the i2c and initialization code.

/**********************************************************************
 * This file uses 2 of the usarts on the discovery board. The first
 *  talks to a computer. The second talks to the Wifly module.
 *
 * Author: Daniel Peterson (dpeterson309@yahoo.com)
 * History: 19 March 2013 - Code started
 *
**********************************************************************/
#include "../inc/stm32f4xx_conf.h"
#include "../inc/stm32f4xx_rcc.h"
#include "../inc/stm32f4xx_gpio.h"
#include "../inc/stm32f4xx_tim.h"
#include "../inc/stm32f4xx_i2c.h"
#include "../inc/misc.h"
#include "../inc/stm32f4xx.h"
#include "../inc/serial.h"
#include "../inc/attitude_hold.h"
#include "../inc/readPacket.h"
 
//make the floating point work
#define _FPU_USED   1
#define _FPU_PRESENT    1 //this should take place in stm32f4xx.h, but it apparently isn't
 
#define BLUE    GPIO_Pin_15
#define RED     GPIO_Pin_14
#define GREEN   GPIO_Pin_12
#define ORANGE  GPIO_Pin_13
 
#define CTLTIM  65536 //frequency the control loop gets called (40 Hz to start)
 
//global variables
//create the structures to hold gyro and accelerometer data
gyro g;
accel a;
motor m;
setPoint s;
 
/** i2c addresses */
#define master 0x0
#define AccelAddress 0x53 //or A6
#define GyroAddress 0x68 //or D0
 
/** global variables */
uint8_t location;
float x_cal, y_cal, z_cal;
 
 
/** setup the io ports */
void Init_IO(void)
{
        GPIO_InitTypeDef GPIOInit;
        //set up the pins with the LED's
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
        GPIOInit.GPIO_Pin = RED | GREEN | ORANGE | BLUE;
        GPIOInit.GPIO_Mode = GPIO_Mode_OUT;
        GPIOInit.GPIO_Speed = GPIO_Speed_50MHz;
        GPIOInit.GPIO_OType = GPIO_OType_PP;
        GPIOInit.GPIO_PuPd = GPIO_PuPd_NOPULL;
 
        GPIO_Init(GPIOD, &GPIOInit);
 
        //setup the USART output pins
        //first one is USART 3, pins pc 10 and 11
        //second is USART 6, pins pc6 and pc7
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART6, ENABLE);
 
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
        GPIOInit.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_10 | GPIO_Pin_11;
        GPIOInit.GPIO_Mode = GPIO_Mode_AF;
        GPIOInit.GPIO_Speed = GPIO_Speed_50MHz;
        GPIOInit.GPIO_OType = GPIO_OType_PP;
        GPIOInit.GPIO_PuPd = GPIO_PuPd_NOPULL;
        GPIO_Init(GPIOC, &GPIOInit);
 
        //usart lines
        GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_USART6);
        GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_USART6);
        GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_USART3);
        GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_USART3);
 
        //setup the the PORTB pins to use timer 3 and control the motors
        //pins 0,1,4,5 are timer 3 channels
        //timer pin order 3,4,1,2 respectively
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
        GPIOInit.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5;
        GPIOInit.GPIO_Mode = GPIO_Mode_AF;
        GPIOInit.GPIO_Speed = GPIO_Speed_50MHz;
        GPIOInit.GPIO_OType = GPIO_OType_PP;
        GPIOInit.GPIO_PuPd = GPIO_PuPd_DOWN;
        GPIO_Init(GPIOB, &GPIOInit);
 
        //motor control lines
        GPIO_PinAFConfig(GPIOB, GPIO_PinSource0, GPIO_AF_TIM3);
        GPIO_PinAFConfig(GPIOB, GPIO_PinSource1, GPIO_AF_TIM3);
        GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_TIM3);
        GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_TIM3);
 
        //i2c stuff
        GPIOInit.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_9;
        GPIOInit.GPIO_Mode=GPIO_Mode_AF;
        GPIOInit.GPIO_PuPd=GPIO_PuPd_NOPULL; //we don't want to pull up because the sensor is 3.3, and this line is 5V.
        GPIOInit.GPIO_Speed = GPIO_Speed_50MHz;
        GPIOInit.GPIO_OType=GPIO_OType_OD; //open drain because i2c is either on or off, No push-pulling
        GPIO_Init(GPIOB, &GPIOInit);
 
        //connect the pins to their alternate functions
        GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1);
        GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_I2C1);
 
 
}
/** configure the interrupt channels */
void Init_NVIC(void)
{
 
    NVIC_InitTypeDef IntInit;
 
    //initialize the priority groups
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
 
    //USART 3 channel
    IntInit.NVIC_IRQChannel = USART3_IRQn;
    IntInit.NVIC_IRQChannelPreemptionPriority = 0;
    IntInit.NVIC_IRQChannelSubPriority = 0;
    IntInit.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&IntInit);
 
    //usart6 channel
    IntInit.NVIC_IRQChannel=USART6_IRQn;
    IntInit.NVIC_IRQChannelPreemptionPriority=0;
    IntInit.NVIC_IRQChannelSubPriority=0;
    IntInit.NVIC_IRQChannelCmd=ENABLE;
    NVIC_Init(&IntInit);
 
    //Timer 4 (control loop) channel
    IntInit.NVIC_IRQChannel=TIM4_IRQn;
    IntInit.NVIC_IRQChannelPreemptionPriority=0;
    IntInit.NVIC_IRQChannelSubPriority=0;
    IntInit.NVIC_IRQChannelCmd=ENABLE;
    NVIC_Init(&IntInit);
 
}
/** configure the timers */
void Init_TIM(void)
{
 
    TIM_TimeBaseInitTypeDef TimBaseInit3; //holds config for timer 3
    TIM_OCInitTypeDef TIMOCInit3; //holds configs for timer 3 output compare
 
    //turn on the timer bus
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
 
    //set up the motor control timer
    TimBaseInit3.TIM_Prescaler = (uint16_t) 83; //clock should run at 1 MHz
    TimBaseInit3.TIM_Period = 5000; //counter should be 200Hz at 100% duty
    TimBaseInit3.TIM_ClockDivision = 0;
    TimBaseInit3.TIM_CounterMode = TIM_CounterMode_Up;
    //now initialize the timer
    TIM_TimeBaseInit(TIM3, &TimBaseInit3);
 
    //set up the output channels
    //every channel has 100% duty at first
    TIMOCInit3.TIM_OCMode = TIM_OCMode_PWM1;
    TIMOCInit3.TIM_OutputState = TIM_OutputState_Enable;
    TIMOCInit3.TIM_OCPolarity = TIM_OCPolarity_High;
    TIMOCInit3.TIM_Pulse = 1000; //1 KHz (or 0% throttle)
 
    //initialize the output compare channels
    TIM_OC1Init(TIM3, &TIMOCInit3);
    TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
 
    TIM_OC2Init(TIM3, &TIMOCInit3);
    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
 
    TIM_OC3Init(TIM3, &TIMOCInit3);
    TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);
 
    TIM_OC4Init(TIM3, &TIMOCInit3);
    TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);
 
 
    //enable the actual counter
    TIM_ARRPreloadConfig(TIM3, ENABLE);
    TIM_Cmd(TIM3, ENABLE);
 
    //use a second timer to regurarly schedule the call to the controller code.
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
 
    //set up the motor control timer
    TimBaseInit3.TIM_Prescaler = (uint16_t) 83; //clock should run at 1 MHz
    TimBaseInit3.TIM_Period = CTLTIM; //get it to take a lot of time so we can measure how long it takes to run 1 iteration of the controller
    TimBaseInit3.TIM_ClockDivision = 0;
    TimBaseInit3.TIM_CounterMode = TIM_CounterMode_Up;
    //now initialize the timer
    TIM_TimeBaseInit(TIM4, &TimBaseInit3);
 
    TIM_ARRPreloadConfig(TIM4, ENABLE);
 
    //TIM4->CR1 |= 0x00000001; //enable the timer
 
}
/** initialize the I2C Interface */
void Init_I2C(void)
{
    //enable the i2c periph
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
 
    I2C_InitTypeDef I2C;
 
    //setup the hardware
    I2C.I2C_ClockSpeed=100000; //200 khz (can go up to 400KHz if needed)
    I2C.I2C_DutyCycle=I2C_DutyCycle_2; //50%
    I2C.I2C_Mode=I2C_Mode_I2C; //put it in I2c Mode
    I2C.I2C_OwnAddress1=0x0; //we are master
    I2C.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit; //we want 7 bit addresses
    I2C.I2C_Ack=I2C_Ack_Disable;
    I2C_Init(I2C1, &I2C);
 
    I2C_Cmd(I2C1, ENABLE); //enable the I2c hardware
}
/** used to initialize the motors */
void Init_Motors(void)
{
    //the esc wants the low speed signal first
    TIM3->CCR1 = 1000;
    TIM3->CCR2 = 1000;
    TIM3->CCR3 = 1000;
    TIM3->CCR4 = 1000;
 
    //pause about half a second
    int i=0;
    while(i<100)
    {
        if (TIM3->SR & 0x0001)
        {
            i++;
            TIM3->SR &= 0xFFFE; //clear this bit
        }
 
    }
    //now command to full throttle for the same amount of time
    TIM3->CCR1 = 2000;
    TIM3->CCR2 = 2000;
    TIM3->CCR3 = 2000;
    TIM3->CCR4 = 2000;
 
    i=0;
    while(i<100)
    {
        if (TIM3->SR & 0x0001)
        {
            i++;
            TIM3->SR &= 0xFFFE; //clear this bit
        }
 
    }
    //now shut them off again
    TIM3->CCR1 = 1000;
    TIM3->CCR2 = 1000;
    TIM3->CCR3 = 1000;
    TIM3->CCR4 = 1000;
}
/** initialize the attitude hold part of the program */
void Init_Control (void)
{
    UpdateSetPoints(0.0,0.0,0.0,0.0); //initially don't try to move
    UpdateGains(1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0);
}
/** some usefult I2C reoutines */
void I2C_Start(I2C_TypeDef* i2c, uint8_t addr, uint8_t dir)
{
    //clear the stop bit
    //i2c->CR1 &= 0xFFFFFDFF;
 
    //wait for the bus to be free (shouldn't take long since we the the only one on it)
    //only check if we are not already master. If we are, we are sending the restart command, so the bus is busy
    if (!(i2c->SR2 & 0x00000001)) while(i2c->SR2 & 0x00000002);
 
    //I2C_GenerateSTART(i2c, ENABLE);
    //send the start condition
    i2c->CR1 |= 0x00000100;
 
    //wait for the start condition to be sent
    while (!(i2c->SR1 & 0x00000001));
 
    uint16_t data = 0;
    //send the slave what we're doing
    if(dir == I2C_Direction_Transmitter)
    {
        data = (addr<<1) & 0xFFFFFE;
        data &= 0x00FF;
    }
    else if (dir == I2C_Direction_Receiver)
    {
        data = (addr<<1) | 0x00000001;
        data &= 0x00FF;
    }
    i2c->DR = data;
 
    //wait until the addr bit is sent (this only gets set if we read for a single byte)
    while (!(i2c->SR1 & 0x00000002));
    if (dir == I2C_Direction_Receiver) i2c->CR1 |= 0x00000400; //enable the ack
    //now check that we are for sure transmitted everything
    if (dir == I2C_Direction_Transmitter) while(!(i2c->SR2 & 0x00000007)); //the last 3 should be set
    else if (dir == I2C_Direction_Receiver) while(!(i2c->SR2 & 0x0000001));
}
/** send data on the line */
unsigned char I2C_Write(I2C_TypeDef* i2c, uint8_t data)
{
    uint16_t send = 0;
    send = data;
    i2c->DR = send & 0x00FF;
    //wait for the acknowledge
    while(!I2C_CheckEvent(i2c, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
        if ((i2c->SR1 & 0x00005F00) > 0) return 1;
        if (i2c->SR1 & 0x00000080) return 2; //if for some reason we didn't transmit
 
    }
    return 0;
}
/** read data from the line */
uint8_t I2C_Read(I2C_TypeDef* i2c)
{
    while(!(i2c->SR1 & 0x00000040));
    // read data from I2C data register and return data byte
    return (uint8_t) (i2c->DR & 0x00FF);
}
/** read multiple */
void I2C_MultiRead(I2C_TypeDef* i2c, uint8_t addr, uint8_t loc, uint8_t size, uint8_t* data)
{
    //we have 3 cases we have to deal with.
    // Case 1: We want 1 byte. This mean no ack
    //Case 2: We want 2 bytes. There is a special procedure for this
    //Case 3: We want more than 2 bytes. This means ack can be set
    uint16_t a=0;
    unsigned char timeout=0; //how many times to retry the bus before giving up
 
    if (size == 1)
    {
         while(i2c->SR2 & 0x00000002); //wait for the bus to be free
         i2c->CR1 &= 0xFFFFFBFF; //disable the acknowledge
         i2c->CR1 |= 0x00000100; //generate a start condition
         while(!(i2c->SR1 & 0x00000001)); //wait for start to be sent
         a = (addr<<1) & 0xFFFFFE;
         a &= 0x00FF;
         i2c->DR=a;
         //wait for the changes to take effect
         while (!(i2c->SR1 & 0x00000002));
         while (!(i2c->SR2 & 0x00000007));
         //send the start location
         I2C_Write(i2c, loc);
         //resend the start condition and address, then read and stop
         i2c->CR1 |= 0x00000100;
         while(!(i2c->SR1 & 0x00000001)); //wait for start to be sent
         a = (addr<<1) | 0x00000001;
         a &= 0x00FF;
         i2c->DR=a;
         while (!(i2c->SR1 & 0x00000002));
         while (!(i2c->SR2 & 0x00000001));
         data=I2C_Read(i2c);
         i2c->CR1 |= 0x00000200; //generate a stop condition
    }
    else if (size == 2)
    {
        while(i2c->SR2 & 0x00000002); //wait for the bus to be free
        i2c->CR1 |= 0x00000400; //enable the acknowledge
        i2c->CR1 |= 0x00000100; //generate a start condition
        while(!(i2c->SR1 & 0x00000001)); //wait for start to be sent
        a = (addr<<1) & 0xFFFFFE;
        a &= 0x00FF;
        i2c->DR=a;
        //wait for the changes to take effect
        while (!(i2c->SR1 & 0x00000002));
        while (!(i2c->SR2 & 0x00000007));
        //send the start location
        I2C_Write(i2c, loc);
        //resend the start condition and address, then read and stop
        i2c->CR1 |= 0x00000100;
        while(!(i2c->SR1 & 0x00000001)); //wait for start to be sent
        a = (addr<<1) | 0x00000001;
        a &= 0x00FF;
        i2c->DR=a;
        while (!(i2c->SR1 & 0x00000002));
        i2c->CR1 &= 0xFFFFFBFF; //disable the ACK
        i2c->CR1 |= 0x00000800; //set the POS bit
        while (!(i2c->SR2 & 0x00000001)); //now ADDR is cleared
        //wait for BTF to be set
        while (!(i2c->SR1 & 0x00000004));
        i2c->CR1 |= 0x00000200; //put in a request for stop
        //read the 2 values
        data[0]=I2C_Read(i2c);
        data[1]=I2C_Read(i2c);
        //clear the pos bit so it doesn't cause problems later
        i2c->CR1 &= 0xFFFFF7FF;
    }
    else
    {
        START: while(i2c->SR2 & 0x00000002); //wait for the bus to be free
        i2c->CR1 |= 0x00000400; //enable the acknowledge
        i2c->CR1 |= 0x00000100; //generate a start condition
        while(!(i2c->SR1 & 0x00000001)); //wait for start to be sent
        a = (addr<<1) & 0xFFFFFE;
        a &= 0x00FF;
        i2c->DR=a;
        //wait for the changes to take effect
        while (!(i2c->SR1 & 0x00000002))
        {
                //if we get a nack, there was an error, Send stop to release the bus and then try again
                if (i2c->SR1 & 0x00000400)
                {
                    i2c->CR1 |= 0x00000200; //generate a stop condition
                    timeout++;
                    if (timeout < 10) goto START;
                    return;
                }
        }
        while (!(i2c->SR2 & 0x00000007));
        //send the start location
        unsigned char s = I2C_Write(i2c, loc);
        if (s > 0)
        {
            i2c->CR1 |= 0x00000200; //generate a stop condition
            i2c->SR1 = 0x00000000; //clear the error flags
            i2c->SR2 = 0x00000000;
            timeout++;
            if (timeout < 10) goto START; //now try again
            return;
        }
        //resend the start condition and address, then read and stop
        i2c->CR1 |= 0x00000100;
        while(!(i2c->SR1 & 0x00000001)); //wait for start to be sent
        a = (addr<<1) | 0x00000001;
        a &= 0x00FF;
        i2c->DR=a;
        while (!(i2c->SR1 & 0x00000002))
        {
            //if we get a nack, there was an error, Send stop to release the bus and then try again
            if (i2c->SR1 & 0x00000400)
            {
                i2c->CR1 |= 0x00000200; //generate a stop condition
                timeout++;
                if (timeout < 10) goto START;
                return;
            }
        }
        while (!(i2c->SR2 & 0x00000001)); //now ADDR is cleared
        //read data until n-3
        uint16_t i=0;
        for (i=0; i < size-3; i++)
        {
            data[i]=I2C_Read(i2c);
        }
        //wait for BTF to be set
        while (!(i2c->SR1 & 0x00000004));
        //set ack to nack
        i2c->CR1 &= 0xFFFFFBFF;
        //read a byte
        data[size-3] = I2C_Read(i2c);
        //again wait for BTF
        while (!(i2c->SR1 & 0x00000004));
        i2c->CR1 |= 0x00000200; //generate a stop condition
        //read the last 2 bytes
        data[size-2]=I2C_Read(i2c);
        data[size-1]=I2C_Read(i2c);
    }
    return;
}
/** write to a certain register on the chip */
void I2C_RegWrite(I2C_TypeDef* i2c, uint8_t sensor, uint8_t loc, uint8_t value)
{
    I2C_Start(i2c, sensor, I2C_Direction_Transmitter);
    I2C_Write(i2c,loc);
    I2C_Write(i2c,value);
    i2c->CR1 |= 0x00000200; //generate a stop condition
}
/** get the accelerometer data into read data */
float GetAccel(int value)
{
  float final_value;
  final_value = (float)value * 0.003910;
  return final_value;
}
/** get gyro data into degrees */
float GetGyro(int value)
{
  float final_value;
  final_value = (float)value * 0.061036;
  return final_value;
}
/** calibrate the accelerometer */
float Calibrate(uint8_t axis)
{
  switch(axis)
  {
    case 'x':
      location = 0x32;
      break;
    case 'y':
      location = 0x34;
      break;
    case 'z':
      location = 0x36;
      break;
  }
 
  uint8_t Cal[2];
  int CalibrateVal;
  int CalibrateTotal;
  float CalibrateAvg;
 
  int i =0;
 for(i=0; i<100; i++)
 {
    I2C_MultiRead(I2C1, AccelAddress, location, 2, Cal);
    CalibrateVal = Cal[1]<<8;
    CalibrateVal |= Cal[0];
    CalibrateTotal += CalibrateVal;
  }
  CalibrateAvg = GetAccel(CalibrateTotal/100);
 
  return CalibrateAvg;
}
/** initialize the IMU */
void Init_IMU(void)
{
 
 
  I2C_RegWrite(I2C1, AccelAddress, 0x2D, 0x00);      //Data Sheet says to put in standyby mode then switch into measure mode for better operation
  I2C_RegWrite(I2C1, AccelAddress, 0x2D, 0x08);
 
  I2C_RegWrite(I2C1, GyroAddress, 0x16, 0x18);      //Sets the gyro for full resolution
  x_cal = -(Calibrate('x'));                      //Sets offset value for x to be negative the current average it EXPECTS to be level IF NOT level, will cause errors
  y_cal = -(Calibrate('y'));                      //
  z_cal = 1-(Calibrate('z'));                     //Sets the offset value for the z axis to 1 since it EXPECTS to be level IF NOT level, will cause errors
}
/** read the IMU */
void ReadIMU(gyro* g, accel* a)
{
    int8_t AccelXYZ[6]={0};
    int A_x, A_y, A_z;
    int8_t GyroXYZ[6]={0};
    int G_x, G_y, G_z;
 
    //first copy all the previous values to their old counterparts
    a->oldx=a->x;
    a->oldy=a->y;
    a->oldz=a->z;
 
    g->oldx=g->x;
    g->oldy=g->y;
    g->oldz=g->z;
 
    //read the accelerometer then the gyro
    //disable serial interrupt while reading i2c. If we interrupt during that, the bus can be left in an unknown state
    USART3->CR1 &= 0xFFFFFFDF;
    I2C_MultiRead(I2C1, AccelAddress, 0x32, 6, AccelXYZ);
    USART3->CR1 |= 0x00000020;
    A_x = AccelXYZ[1]<<8;
    A_x |= AccelXYZ[0];
    A_y = AccelXYZ[3]<<8;
    A_y |= AccelXYZ[2];
    A_z = AccelXYZ[5]<<8;
    A_z |= AccelXYZ[4];
 
    a->x = GetAccel(A_x) + x_cal;
    a->y = GetAccel(A_y) + y_cal;
    a->z = GetAccel(A_z) + z_cal;
 
    USART3->CR1 &= 0xFFFFFFDF;
    I2C_MultiRead(I2C1, GyroAddress, 0x1D, 6, GyroXYZ);
    USART3->CR1 |= 0x00000020;
    G_x = GyroXYZ[0]<<8;
    G_x |= GyroXYZ[1];
    G_y = GyroXYZ[2]<<8;
    G_y |= GyroXYZ[3];
    G_z = GyroXYZ[4]<<8;
    G_z |= GyroXYZ[5];
    g->x = GetGyro(G_x);
    g->y = GetGyro(G_y);
    g->z = GetGyro(G_z);
 
}
void UpdateMotors(motor* m)
{
    TIM3->CCR3 = 1000+m->wl; //PB0 (left b/c ccw)
    TIM3->CCR4 = 1000+m->wf; //PB1 (front b/c cw)
    TIM3->CCR1 = 1000+m->wb; //PB4 (back b/c cw)
    TIM3->CCR2 = 1000+m->wr; //PB5 (right b/c ccw)
}
/** now the main function of the program */
int main (void)
{
    *((volatile unsigned long*)0xE000ED88) = 0xF << 20; //enable the FPU
    Init_IO();
    Init_NVIC();
    Init_USART();
    Init_TIM();
    Init_I2C();
    sendString3("Initialization Complete\n");
    Init_Motors();
    sendString3("Motors ready\n");
    Init_IMU();
    sendString3("IMU Calibrated\n");
    Init_Control();
    sendString3("Control initialized\n");
 
    //get the initial IMU readings
    a.dt = 0.025;
    g.dt = 0.025;
 
    while (1)
    {
        //check if we have a control packet
        sendString3("h\n");
        if(getBytesAvaiable3() > 9)
        {
            sendString3("Got it\n");
            readPacket(&s);
            UpdateSetPoints(s.phi, s.theta, s.psi, s.vx);
        }
        ReadIMU(&g, &a);
        UpdateControl(&g,&a,&m);
        UpdateMotors(&m);
    }
    return 0;
}
/** update the main control loop periodically based on the TIM4 Interrupt */
/**void TIM4_IRQHandler (void)
{
    if (TIM4->SR & 0x00000001)
    {
        TIM_ITConfig(TIM4, TIM_IT_Update, DISABLE);
            ReadIMU(&g, &a);
        UpdateControl(&g,&a,&m);
        UpdateMotors(&m);
        TIM4->SR &= 0xFFFFFFFE; //clear the flag
        //reset the counter
        TIM4->CNT=0;
        //reenable the interrupt  (make it do it doesn't fire too often)
        TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
    }
}*/

And here's the serial code (I have the stuff in there for 2 usarts, but I'm only actually using USART3)

/** built to hold my serial buffer functions in the hopes it fixes all the weird linker errors */
#include "../inc/serial.h"
 
//serial buffer variables
#define sBuffSize 128
//usart 3 first
uint8_t sBuff3[sBuffSize];
uint16_t bytesRecieved3;
uint16_t bytesRead3;
char overflow3;
 
//usart 6
uint8_t sBuff6[sBuffSize];
uint16_t bytesRecieved6;
uint16_t bytesRead6;
char overflow6;
 
 
/** interupt handler routines */
void USART3_IRQHandler(void)
{
    if (USART3->SR & 0x00000020) //check the receive interrupt
    {
        USART3->CR1 &= 0xFFFFFFDF;//disable the interrupt
        unsigned char cnt=0;
        unsigned char s=0;
        //read the first byte and wait for the second
        sBuff3[bytesRecieved3] = (uint8_t) (USART3->DR & 0x000000FF); //grab the latest packet
        s=sBuff3[bytesRecieved3];
        if (s != 0xAF) goto END;
        bytesRecieved3++;
        if (bytesRecieved3 > sBuffSize)
        {
            bytesRecieved3 = 0; //return the counter to 0 if the buffer overflows; an error will occur when checking packets, signaling an overflow
            overflow3 = 1; //set the buffer overflow flag
        }
        while(!(USART3->SR & 0x00000020));
        cnt=(unsigned char)(USART3->DR & 0x000000FF);
        while (cnt > 0)
        {
            while(!(USART3->SR & 0x00000020));
            sBuff3[bytesRecieved3] = (uint8_t) (USART3->DR & 0x000000FF); //grab the latest packet
            bytesRecieved3++;
            cnt--;
            if (bytesRecieved3 > sBuffSize)
            {
                bytesRecieved3 = 0; //return the counter to 0 if the buffer overflows; an error will occur when checking packets, signaling an overflow
                overflow3 = 1; //set the buffer overflow flag
            }
        }
        END: USART3->CR1 |= 0x00000020;
    }
}
/** configure the serial ports */
void Init_USART(void)
{
    USART_InitTypeDef USARTInit;
 
    USARTInit.USART_BaudRate=9600;
    USARTInit.USART_WordLength=USART_WordLength_8b;
    USARTInit.USART_StopBits=USART_StopBits_1;
    USARTInit.USART_Parity=USART_Parity_No;
    USARTInit.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
    USARTInit.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //enable TX and RX (TX=8H, RX=4H);
    USART_Init(USART3, &USARTInit);
 
    //setup and enable the serial interrupts
    USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
 
    //enable the USART
    USART_Cmd(USART3, ENABLE);
 
    //now the second USART
    //USARTInit.USART_BaudRate=19200;
    //USARTInit.USART_WordLength=USART_WordLength_8b;
    //USARTInit.USART_StopBits=USART_StopBits_1;
    //USARTInit.USART_Parity=USART_Parity_No;
    //USARTInit.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
    //USARTInit.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //enable TX and RX (TX=8H, RX=4H);
    //USART_Init(USART6,&USARTInit);
 
    //setup and enable the serial interrupts
//  USART_ITConfig(USART6, USART_IT_RXNE, ENABLE);
 
 
    //enable the USART
//  USART_Cmd(USART6, ENABLE);
}
 
/** serial access routines */
//usart3 first
/** read a single value from the buffer */
uint16_t readSerial3(void)
{
    uint16_t value;
    if ((bytesRead3 < bytesRecieved3) && !overflow3)
    {
        value = sBuff3[bytesRead3]; //start reading at bytesRead (it stores where it stopped reading, so it should be  behind the bytesRecieved counter
        bytesRead3++;
        if (bytesRead3 > sBuffSize) bytesRead3 = 0; //go back to the beginning if we have reached the end
    }
    else if((bytesRead3 > bytesRecieved3) && overflow3)
    {
        value = sBuff3[bytesRead3]; //start reading at bytesRead (it stores where it stopped reading, so it should be  behind the bytesRecieved counter
        bytesRead3++;
        if (bytesRead3 > sBuffSize)
        {
            bytesRead3 = 0; //go back to the beginning if we have reached the end
            overflow3 = 0; //reset the overflow flag because the counter overflowed as well
        }
    }
    else value = 0; //if reader has caught up to the buffer, just return 0;
    return value;
}
/** get how much data is buffered. That way we're not reading an empty buffer */
uint16_t getBytesAvaiable3(void)
{
    //make sure to return the correct value if the buffer has started over
    if (bytesRead3 <= bytesRecieved3) return (bytesRecieved3 - bytesRead3);
    else return ((sBuffSize - bytesRead3) + bytesRecieved3);
}
void sendString3(const char *data)
{
    while (*data)
    {
        //wait for the previous byte to be transmitted (basically block until I get a buffer implemented)
        USART3->DR = (*data)&0x000000FF;
        data++;
 
        while (!(USART3->SR & 0x00000040));
    }
}

Any thoughts on what could be wrong are appreciated. I needed this working yesterday (its for senior design), and I'm stumped at to what could be wrong. I can't get the error to repeat consistently. It always shows up, but sometimes right away, sometimes after a few minutes. I have no clue.

Outcomes