Tuesday, December 28, 2010

STM32F103 System Reset

OK, a tough one to find.

If you want to cause the processor to reset its self, you are looking for NVIC_SystemReset in the core_cm3.h file!

TF

Friday, December 17, 2010

STM32F103 SPI (SSI) Bus Code

So,
The docs that come from ST Micro are pretty good as far as listing methods, and all the info is there.  The code examples that come with  the Standard Peripheral Library are nifty, but nowhere are there explanations of how things work.  The code examples all use Interrupts or DMA.  Those are nifty once everything works, but with a new board and chips one needs to be able to test things in a polling fashion.  Also we use RTOS.


First, I have a struct for each SPI device, and use SPI1 for most devices, and SPI2 for a particular device that requires very frequent (6000 per second) access.

typedef struct SSIConfigStruct {
    // Arbitrary name of the device. e.g. "GYROS"
    char name[32];
    xSemaphoreHandle semaphore;
    SPI_InitTypeDef spiInitStruct;
    SPI_TypeDef * spiDevice;
    // The chip enable for the target slave device.
    // SPI chip enables are inverted.
    uint16_t GPIO_Pin;
    GPIO_TypeDef * GPIO_Port;
} xSSIConfig;

Each SPI bus has a lock semaphore to control acccess.

And here are the globals...
xSemaphoreHandle SSILock1;
xSemaphoreHandle SSILockGyros;
xSSIConfigHandle SSIDevices[SSI_DEVICE_COUNT];
// All except gyros.
SPI_InitTypeDef SPI1_InitStructure;
// Just the gyros.
SPI_InitTypeDef SPIGyros_InitStructure;
unsigned char * ssi1Lockee = "Uninitialized";
unsigned char * ssi2Lockee = "Uninitialized";
The Lockee pointers are set when a device gets selected and the lock is acquired so in debugging I know who did what.

So initializing one of the SPI busses is like this...
  ssi1Lockee = "Unused";
  
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |
                         RCC_APB2Periph_GPIOB |
                           RCC_APB2Periph_AFIO |
                             RCC_APB2Periph_SPI1,
                             ENABLE);
 
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
 
  // The general use SPI bus. SPI1
  vSemaphoreCreateBinary(SSILock1);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  SPI_StructInit(&SPI1_InitStructure);
  SPI1_InitStructure.SPI_Mode = SPI_Mode_Master;
  SPI1_InitStructure.SPI_NSS = SPI_NSS_Soft;
  SPI1_InitStructure.SPI_CPOL = SPI_CPOL_Low;
  SPI1_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
  // of SPI_BaudRatePrescaler_64 results in a clock rate of 1.1 Mhz
  // (Measured with oscilloscope.)
  SPI1_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;
  SPI_Init(SPI1 , &SPI1_InitStructure);
  SPI_Cmd(SPI1, ENABLE);
 
  // The 3 gyros on their own private SPI bus. SPI2
  vSemaphoreCreateBinary(SSILockGyros);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  SPI_StructInit(&SPIGyros_InitStructure);
  SPIGyros_InitStructure.SPI_Mode = SPI_Mode_Master;
  SPIGyros_InitStructure.SPI_NSS = SPI_NSS_Soft;
  // of SPI_BaudRatePrescaler_16 results in a clock rate of 2.2 Mhz
  // (Measured with oscilloscope.)
  SPIGyros_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
  SPI_Init(SPI2 , &SPIGyros_InitStructure);
  SPI_Cmd(SPI2, ENABLE);
 
(removed code ... initialize each sub device on the SPI bus ... )
  Notice that you first enable clocking to the IO ports and peripheral devices.  Not having clocks on save power on the chip.  Also, the chip enables are low asserted and hooked to various bits on the GPIOE port.

Initializing a single device is like this...
  // The RESET line on the PIN ASIC. Normally low, toggle high then low to reset
  // and start next sample.  Pin PE7
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOE, &GPIO_InitStructure);
  GPIO_WriteBit(GPIOE, GPIO_Pin_7, Bit_RESET);
 
  SSIDevices[SSI_DEVICE_PNI11096] = &PNI11096SSIConfig;
  PNI11096SSIConfig.GPIO_Port = GPIOE;
  PNI11096SSIConfig.GPIO_Pin = GPIO_Pin_11;
  strncpy_safe(PNI11096SSIConfig.name, "PNIC", 32);
  PNI11096SSIConfig.spiDevice = SPI1;
  PNI11096SSIConfig.semaphore = SSILock1;
  SPI_StructInit(&PNI11096SSIConfig.spiInitStruct);
  PNI11096SSIConfig.spiInitStruct.SPI_DataSize = SPI_DataSize_8b;
  // TODO
  // Max is 1 MHz
  SPI_InitGPIOPin(&PNI11096SSIConfig);
The big secret here is that to change clock rate or between 8 bit and 16 bit requires that you disable the SPI bus, make the change, and enable the bus again.
So the support routines look like this...

// Delay loop is about 6 instructions per loop.
void SSI_Delay(int n)
{
  volatile int SPI_DelayCount;
  for(SPI_DelayCount=0; SPI_DelayCount < n; SPI_DelayCount++)
  {
  }
}
void SPI_InitGPIOPin(xSSIConfigHandle h)
{
  GPIO_InitStructure.GPIO_Pin = h->GPIO_Pin;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOE, &GPIO_InitStructure);
  GPIO_WriteBit(h->GPIO_Port, h->GPIO_Pin, Bit_SET);
}

// Make the device be the current device.
void SSI_SwitchTo(int deviceIdx, unsigned char * lockee)
{
  xSSIConfigHandle h = SSIDevices[deviceIdx];
 
  if(h->spiDevice == SPI1)
  {
    ssi1Lockee = lockee;
  }
  else if(h->spiDevice == SPI2)
  {
    ssi2Lockee = lockee;
  }
  SPI_Cmd(SPI1, DISABLE);
  SPI_DataSizeConfig(h->spiDevice, h->spiInitStruct.SPI_DataSize);
  // TODO - Set click divisor to get correct frequency.
  SPI_Cmd(SPI1, ENABLE);
 
  GPIO_ResetBits(h->GPIO_Port, h->GPIO_Pin);
}

void SSI_UnSwitch(int deviceIdx)
{
  xSSIConfigHandle h = SSIDevices[deviceIdx];
 
  GPIO_SetBits(h->GPIO_Port, h->GPIO_Pin);
  if(h->spiDevice == SPI1)
  {
    ssi1Lockee = "None";
  }
  else if(h->spiDevice == SPI2)
  {
    ssi2Lockee = "None";
  }
}

volatile short ittibits;
// Enables the device, sends data, waits for it to finish sending, gets results.
// Disables the device after the read.
unsigned short SSI_SendDataAndWaitAndDone(int deviceIdx, unsigned short data)
{
  short ret;
  xSSIConfigHandle h = SSIDevices[deviceIdx]; 
  // Clear any latent recieved data just in case. 
 SPI_I2S_ReceiveData (h->spiDevice);
 
  GPIO_ResetBits(h->GPIO_Port, h->GPIO_Pin);
  // 75Mhz / 6 cycles so this is a little under 1 microsecond.
  SSI_Delay(1);
  SPI_I2S_SendData (h->spiDevice, data);
  while (SPI_I2S_GetFlagStatus(h->spiDevice, SPI_I2S_FLAG_TXE) == RESET)
  {
    portYIELD();
  }
  while (SPI_I2S_GetFlagStatus(h->spiDevice, SPI_I2S_FLAG_RXNE) == RESET)
  {
    portYIELD();
  }
  ret = SPI_I2S_ReceiveData (h->spiDevice);
  // So we can debug the value.
  ittibits = ret;
  GPIO_SetBits(h->GPIO_Port, h->GPIO_Pin);
  SSI_Delay(1);
  return ret;
}

// Enables the device, sends data, waits for it to finish sending, gets results.
// Does NOT disable the device after the read.
unsigned short SSI_SendDataAndWaitAndNotDone(int deviceIdx, unsigned short data)
{
  unsigned short ret;
  xSSIConfigHandle h = SSIDevices[deviceIdx];
  ittibits = data;
 
  SPI_I2S_ReceiveData (h->spiDevice);
 
  GPIO_ResetBits(h->GPIO_Port, h->GPIO_Pin);
  SSI_Delay(1);
  SPI_I2S_SendData (h->spiDevice, data);
  while (SPI_I2S_GetFlagStatus(h->spiDevice, SPI_I2S_FLAG_TXE) == RESET)
  {
    portYIELD();
  }
  while (SPI_I2S_GetFlagStatus(h->spiDevice, SPI_I2S_FLAG_RXNE) == RESET)
  {
    portYIELD();
  }
  ret = SPI_I2S_ReceiveData (h->spiDevice);
  ittibits = ret;
  return ret;
}

unsigned short SSI_LockSendDataAndWaitAndDone(int deviceIdx, unsigned short data)
{
  xSSIConfigHandle h = SSIDevices[deviceIdx];
  unsigned short ret;
 
  xSemaphoreTake(h->semaphore, portMAX_DELAY);
  {
    SPI_I2S_ReceiveData (h->spiDevice);
  
    GPIO_ResetBits(h->GPIO_Port, h->GPIO_Pin);
    // 75Mhz / 6 cycles so this is a little under 1 microsecond.
    SSI_Delay(1);
    SPI_I2S_SendData (h->spiDevice, data);
    while (SPI_I2S_GetFlagStatus(h->spiDevice, SPI_I2S_FLAG_TXE) == RESET)
    {
      portYIELD();
    }
    while (SPI_I2S_GetFlagStatus(h->spiDevice, SPI_I2S_FLAG_RXNE) == RESET)
    {
      portYIELD();
    }
    ret = SPI_I2S_ReceiveData (h->spiDevice);
    // So we can debug the value.
    ittibits = ret;
    GPIO_SetBits(h->GPIO_Port, h->GPIO_Pin);
    SSI_Delay(1);
  }
  xSemaphoreGive(h->semaphore);
 
  return ret;
}

// Enables the device, sends data, waits for it to finish sending, gets results.
// Does NOT disable the device after the read.
unsigned short SSI_LockSendDataAndWaitAndNotDone(int deviceIdx, unsigned short data)
{
  xSSIConfigHandle h = SSIDevices[deviceIdx];
  unsigned short ret;
 
  xSemaphoreTake(h->semaphore, portMAX_DELAY);
  {
    SPI_I2S_ReceiveData (h->spiDevice);
  
    GPIO_ResetBits(h->GPIO_Port, h->GPIO_Pin);
    SSI_Delay(1);
    SPI_I2S_SendData (h->spiDevice, data);
    while (SPI_I2S_GetFlagStatus(h->spiDevice, SPI_I2S_FLAG_TXE) == RESET)
    {
      portYIELD();
    }
    while (SPI_I2S_GetFlagStatus(h->spiDevice, SPI_I2S_FLAG_RXNE) == RESET)
    {
      portYIELD();
    }
    ret = SPI_I2S_ReceiveData (h->spiDevice);
    ittibits = ret;
  }
  xSemaphoreGive(h->semaphore);
 
  return ret;
}

TF 

Wednesday, October 27, 2010

Transcendent Wonder

It's fall and it just snowed.

So I had to get up in the dark and shovel the driveway, and the snow was so heavy I finally got out the snow blower.  So there I was feeling sorry for my self.  All this hard work and I just wanted to sleep.

When I got done I came inside and started breakfast for the kids, and decided to do the dishes too.
I was feeling even more angry, depressed, it's dark out, cold, no help...

    I stopped and I looked
    Each drop of water, unique.
    Hands washing plates clean.


All the anger and self pity evaporated.

Transcendent Wonder.

It's all around us all of the time.

TF

Tuesday, October 26, 2010

The Depth of Children

So, this morning while getting ready for school, my 12 year old son asked,
"Why do people have syntax in speaking when no other animal does."

Wow, the other day we were talking about what is unique about humans and the main thing I told him was that we have syntax in language, and no other animal does.
So then he looked it up on Wikipedia yesterday, "syntax", and then asked the question.
So this morning we discussed evolution and how every trait is either useful or vestigial.
Syntax is useful because of very precise communication and survival.
Tail bones in humans are vestigial.

Then got onto the anthropic principle and how the world is the way it is because if it wasn't we wouldn't be here to ask why it is the way it is. Syntax is much like that.  If we didn't have syntax we wouldn't be able to think about thinking and ask such a question.

Never underestimate the intelligence and depth of children!

TF

Monday, October 25, 2010

How Cool are Wavelets

Ok, Todays topic, Wavelets.
Wavelets can be used to detect wave forms of some shape and frequency.
This is much like a poor man's FFT.

Wavlets work like this...
You have a 'template' wave shape that you are trying to detect.
For example a wave might look like this...

This is a kind of random wave shape but lets assume it is important to detect when this wave form happens in some data stream.

The incoming data stream might look like this...

The red wave to the right is the input data.
Hidden in there is the blue wave form with some noise.

What we do is convolute the input wave form with the wavelet. By convolute we mean take each point in the wavelet and multiply it by the corresponding point in the input data stream and do that for every possible position along the input data stream.
Thus we 'scan' the input stream with the wavelet generating a correlation factor for each point along the input data stream.

Like this...

for i=0 to in.length
sum[i] = 0;
for k=0 to wavlet.length
sum[i] += in[i] * wavelet[k];

Then the sum array will tell you how much a given point in the input correlates to the wavelet form.

If you study this algorithm you will see that where the wavelet is high and the input is high you get a large positive number. If the wavelet is negative and the input is negative you again get a large positive number.

If the input is zero and the wavelet is not zero
you still get zero, and also if the wavelet is zero and the input is not zero you still get zero.

If the input is the inverse of the wavelet then the
correlation will be negative.

A few notes: The wavelet must be normalized. The ideal is that the peak wavelet value is 1.0. Or you can make the integral of the wavelet be zero. It depends on what result you want.

The wavelet also has a frequency. You can stretch
or shrink the wavelet length to detect different frequencies of the wave form.

A specific frequency detector wavelet looks like this...


This wavelet will detect a specific frequency. The more cycles in the wave the narrower the
frequency detection.

This wavelet is a low pass filter...
And this is a high pass filter...

The cool thing here is that the wavelet can detect a specific wave shape and timing without the more complex math of a complete FFT. Also with out knowing exact frequencies and such, you can detect a specific wave shape.

Very useful for heart beat detection, specific vibration modes, and many other uses.

There have been zillions of academic papers written analyzing specific wave shapes and exactly how they filter or detect frequencies. But to use wavelets it is not necessary to understand all the theory.

TF

Friday, April 30, 2010

BMP085 C Code for ARM Processor

I have debugged this code for the BMP085 pressure and temperature sensor.

(Note: I found this but do not yet have source: More precise calcs)

The examples in the data sheet assume an incorrect C operartor order.

Also in Arduino kits the int is a short and a long is a 32 bit int.

On my processor ( LM3S1607 ) an int is 32 bits.

So in this code everything is an int or unsigned int. Never short or unsigned short.

Also Arduino example code assumes the oversampling to be 0 and here we can set it to higher resolution.

The b3 = ... is where parenthesis are needed to get it right.

Enjoy.



int x1, x2, x3, b3, b5, b6, p;

unsigned int b4, b7;


x1 = ((BaroState.rawTemperature - Baro_ac6) * Baro_ac5) >> 15;
x2 = (Baro_mc << b5 =" x1" temperaturetenthc =" (b5">> 4;

b6 = b5 - 4000;
x1 = (Baro_b2 * (b6 * b6 >> 12)) >> 11;
x2 = Baro_ac2 * b6 >> 11;
x3 = x1 + x2;
// Error in the BMP085 spec sheet math.  Extra parens needed at the V's
//    V                              V
b3 = (((Baro_ac1 * 4 + x3) << Baro_oss) + 2) >> 2;
x1 = Baro_ac3 * b6 >> 13;
x2 = (Baro_b1 * (b6 * b6 >> 12)) >> 16;
x3 = ((x1 + x2) + 2) >> 2;
b4 = (Baro_ac4 * (x3 + 32768)) >> 15;
b7 = (BaroState.rawPressure - b3) * (50000 >> Baro_oss);
p = b7 < x1 =" (p">> 8) * (p >> 8);
x1 = (x1 * 3038) >> 16;
x2 = (-7357 * p) >> 16;
BaroState.pressurePa = p + ((x1 + x2 + 3791) >> 4);
NavState.baro = BaroState.pressurePa;

Saturday, January 16, 2010

LM3S1607 internal clock set

On the stellaris chips setting the clock up can be a challenge.

The prototype boards use external clock crystals.
The setup is like this...

SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);

To get the internal clock I use this...

SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_INT);

TF

More...

Watch out! The internal oscillator on the LM3S1607 is only +- 30 % at 12 Mhz.
That means that UARTS will not work reliably at all.
So thats why the eval board has a crystal on it.

TF

Thursday, January 7, 2010

UART1 on LM3S9B92

Ok,
I just spent 2 days hammering away at getting UART1 to put out a signal on the TI LM3S9B92-B eval board.

The sample code called hello.c talks to UART0 and works fine.

If you try to switch to UART1 or UART2 it does not work.
SO I extracted the driverlib calls out to an easily switchable linear piece of code, like this...
(The blog post code mangles the C code a bit. The for loops are munged.)

int
main(void)
{
int uartIdx = 1;
volatile unsigned long i;
SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |
SYSCTL_XTAL_16MHZ);

if(uartIdx == 0)
{
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
UARTConfigSetExpClk(UART0_BASE, SysCtlClockGet(), 115200,
(UART_CONFIG_PAR_NONE | UART_CONFIG_STOP_ONE |
UART_CONFIG_WLEN_8));
}
else
{
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
GPIOPinTypeUART(GPIO_PORTD_BASE, GPIO_PIN_2 | GPIO_PIN_3);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);
UARTConfigSetExpClk(UART1_BASE, ROM_SysCtlClockGet(), 115200,
(UART_CONFIG_PAR_NONE | UART_CONFIG_STOP_ONE |
UART_CONFIG_WLEN_8));
}

for(i=0; i<100000;>
{
}
//
while(1)
{
if(uartIdx == 0)
{
UARTCharPut(UART0_BASE, 'K');
}
else
{
UARTCharPut(UART1_BASE, 'K');
}
for(i=0; i<100000; i =" i"> {
}
}
}

But UART1 was stubbornly silent.

Here is the corrected code, notice that I have to explicitly set the pins up...

int
main(void)
{
int uartIdx = 1;
volatile unsigned long i;
SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |
SYSCTL_XTAL_16MHZ);

if(uartIdx == 0)
{
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
UARTConfigSetExpClk(UART0_BASE, SysCtlClockGet(), 115200,
(UART_CONFIG_PAR_NONE | UART_CONFIG_STOP_ONE |
UART_CONFIG_WLEN_8));
}
else
{
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
GPIOPinTypeUART(GPIO_PORTD_BASE, GPIO_PIN_2 | GPIO_PIN_3);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);
UARTConfigSetExpClk(UART1_BASE, ROM_SysCtlClockGet(), 115200,
(UART_CONFIG_PAR_NONE | UART_CONFIG_STOP_ONE |
UART_CONFIG_WLEN_8));
GPIOPinConfigure(GPIO_PD2_U1RX);
GPIOPinConfigure(GPIO_PD3_U1TX);
}

for(i=0; i<100000;i++>
{
}
//
while(1)
{
if(uartIdx == 0)
{
UARTCharPut(UART0_BASE, 'K');
}
else
{
UARTCharPut(UART1_BASE, 'K');
}
for(i=0; i<100000;i> {
}
}
}

I think what is happening is that the in circuit debugger sets up the UART0 since that is used often in sample code. None of the example code shows this solution.

Any way, I hope I have saved someone some time.

TF

P.S. The signals out of the chip are inverted from standard RS232. It is expected that your circuit will use drivers that convert the 0 to 3 volt TTL output to inverted -5 to + 5 volt RS232.
Thus if you directly wire a DB9 connector to a chip port and then into a PC serial port, it will not work.