Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 2 | ||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
| ||||||
|
Having a little bit of experience in signal processing and stuff, I thought I might try out acoustic localisation using microphones.
The setup is straightforward and we use it all the time to determine locations of sounds through our ears. The difference of course is that this is being done by using "cheap" off-the-shelf components from ebay, so this makes it all good in theory, but does it actually work? Cutting to the chase, it is a definite yes.
Front End
The front end consists of two microphone modules based upon the max9812 chip that give a fixed gain of 20dB. Ideally it would be better if the gain was somehow programmable, because I'm using the 12bit ADCs (analogue to digital converters) on the launchpad. The signals are fed into an op amp based 2nd order low pass filter. This is a low pass filter for anti aliasing that is required when using ADCs. Naturally I had cheap ceramic capacitors rated at +/- 20% of their value, so hmmm. I used four 1uF decoupling capacitors and biased at 1.65V for input to the ADC.
My biggest problem with my front end was noise from the servo, both electrical and mechanical. To address this issue, I separated the ground and the power (the servo uses 5V in any case). I also loaded up the bread board with a dozen or so capacitors between ground and 3.3V. Incidentally, I used a second op amp to convert the 3.3V servo signal to 5V by driving the op amp to the rail.
TivaC
The TivaC is a Texas Instruments launchpad board that runs a TM4C123GH6PM processor that has a 80Mhz 32 bit Cortex-M4 core.
The TM4C123GH6PM has actually only 2 physical ADCs, but you can multiplex the input signals to each of them so as in this case, they are processing eight inputs each. I'm running both at max speed (1 mega sample per second each), adding them both together, doing an 8 times hardware averaging, and doing a micro DMA to get to the digital filtering stage.
The digital filter runs on a circular buffer and uses 63 taps. The filter is designed to filter out above 4kHz to remove noise.
From there it goes straight into the USB buffer to be sent out as soon as it gets the request from the PC.
The USB protocol I am using is very very simple. The PC sends the Tiva a 6 byte packet consisting of the servo position and how many bytes it wants. The Tiva responds with a packet of 16 bit ADC values (in practice a packet of 8192 bytes about 30 times per second).
The TivaC is also configured to spit out debug info from the uart. This proved invaluable in rooting out buffer overflows.
It also drives a PWM signal to the servo.
I've included a copy of the main code that drives the TivaC so you can get an idea how it is done.
PC software
The USB driver I am using is the WinUSB with TI's Generic Bulk Device driver.
The actual PC software is in written as both a native C++ DLL (that connects to the device and does the time domain correlation) and a C# based user interface exe. The C# exe uses "interop" to connect to the native DLL. Of course the PC is running Windows 10, not sure whether that means Windows IOT, but I guess in this broad definition of IoT, call it maybe.
Cross correlation in the time domain is very simple. It is simply a number of running sums of the two microphone signals multiplied with each other with each a different time delay. The time delay that produces the highest sum is the peak and from that a phase delay between the two signals can be determined.
correlation (time, n) = correlation (time - 1, n) * decay + microphone_left(time) * microphone_right(time-n) where n is the phase delay
The high sample rate of near 16kHz allows a resolution of around 2 degrees.
I'm using Markov probabilistic chaining to combine the belief that the sound source is likely not to have moved. This seems to work well enough. Obviously multiple microphones (hence the 8 channels) would be better.
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_ints.h"
#include "inc/hw_types.h"
#include "inc/hw_adc.h"
#include "driverlib/pin_map.h"
#include "driverlib/debug.h"
#include "driverlib/sysctl.h"
#include "driverlib/adc.h"
#include "driverlib/rom.h"
#include "driverlib/interrupt.h"
#include "driverlib/gpio.h"
#include "driverlib/timer.h"
#include "driverlib/udma.h"
#include "driverlib/uart.h"
#include "usblib/usblib.h"
#include "usblib/usb-ids.h"
#include "usblib/device/usbdevice.h"
#include "usblib/device/usbdbulk.h"
#include "utils/uartstdio.h"
#include "utils/ustdlib.h"
#include "usb_bulk_structs.h"
#include "driverlib/pwm.h"
#include "global_def.h"
/*
* main.c
*/
#define PWM_FREQUENCY (50)
#define PWM_MIN (30)
#define PWM_MAX (120)
#define PWM_ZERO (75)
static volatile uint32_t g_ui32DMAErrCount = 0;
void InitADC00(void);
void ConfigureUART(void);
static volatile bool g_bUSBConfigured = false;
#pragma DATA_ALIGN(g_adcPingPong0, 64)
static int32_t g_adcPingPong0[ADC_BUFFER_SIZE*ADC_BUFFER_WINDOW_COUNT][NUM_CHANNELS];
static int32_t g_adcInIndex0 = 0;
static int32_t g_adcOutIndex0 = 0;
static volatile int32_t g_stall0 = 0;
static volatile int32_t g_adcPingPongStage0[ADC_BUFFER_WINDOW_COUNT] = { 0 };
#pragma DATA_ALIGN(g_adcPingPong1, 64)
static int32_t g_adcPingPong1[ADC_BUFFER_SIZE*ADC_BUFFER_WINDOW_COUNT][NUM_CHANNELS];
static int32_t g_adcInIndex1 = 0;
static volatile int32_t g_stall1 = 0;
static volatile int32_t g_adcPingPongStage1[ADC_BUFFER_WINDOW_COUNT] = { 0 };
static uint32_t g_msg_index = 0;
static uint32_t g_usb_receive_size = 0;
static volatile int32_t g_usb_bytes_to_read = 0;
static uint32_t g_ui32Load;
static uint32_t g_ui32PWMClock;
static uint8_t g_ui8Adjust = PWM_ZERO;
static volatile uint8_t g_ui8AdjustPending = PWM_ZERO;
static int32_t g_channelSum[NUM_CHANNELS] = { 0 };
static int16_t g_channelOffset[NUM_CHANNELS] = { 0 };
static int32_t g_channelOffsetCount = 0;
static uint8_t g_fire = 0;
static int32_t g_filter[FILTER_SIZE] =
{
2158 ,
-3620 ,
-7100 ,
-4486 ,
1833 ,
2758 ,
-2365 ,
-3667 ,
2091 ,
4641 ,
-1618 ,
-5790 ,
807 ,
7030 ,
412 ,
-8308 ,
-2119 ,
9586 ,
4462 ,
-10802 ,
-7643 ,
11929 ,
12065 ,
-12913 ,
-18596 ,
13723 ,
29512 ,
-14331 ,
-53266 ,
14702 ,
166092 ,
247319 ,
166092 ,
14702 ,
-53266 ,
-14331 ,
29512 ,
13723 ,
-18596 ,
-12913 ,
12065 ,
11929 ,
-7643 ,
-10802 ,
4462 ,
9586 ,
-2119 ,
-8308 ,
412 ,
7030 ,
807 ,
-5790 ,
-1618 ,
4641 ,
2091 ,
-3667 ,
-2365 ,
2758 ,
1833 ,
-4486 ,
-7100 ,
-3620 ,
2158
};
// uDMA control table aligned to 1024-byte boundary
#pragma DATA_ALIGN(pui8ControlTable, 1024)
uint8_t pui8ControlTable[1024];
#if ((ADC_BUFFER_SIZE * (ADC_BUFFER_WINDOW_COUNT - 1)) < FILTER_SIZE)
error
#endif
int main(void)
{
//uint32_t ui32Period;
SysCtlClockSet(SYSCTL_SYSDIV_2_5|SYSCTL_USE_PLL|SYSCTL_OSC_MAIN|SYSCTL_XTAL_16MHZ);
SysCtlPWMClockSet(SYSCTL_PWMDIV_64);
// configure USB
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
ROM_GPIOPinTypeUSBAnalog(GPIO_PORTD_BASE, GPIO_PIN_4 | GPIO_PIN_5);
ConfigureUART();
// gpio
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3);
GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE, GPIO_PIN_7);
GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_7, 0);
// configure pwm
SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);
GPIOPinTypePWM(GPIO_PORTB_BASE, GPIO_PIN_6);
GPIOPinConfigure(GPIO_PB6_M0PWM0);
g_ui32PWMClock = SysCtlClockGet() / 64;
g_ui32Load = (g_ui32PWMClock / PWM_FREQUENCY) - 1;
PWMGenConfigure(PWM0_BASE, PWM_GEN_0, PWM_GEN_MODE_DOWN);
PWMGenPeriodSet(PWM0_BASE, PWM_GEN_0, g_ui32Load);
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0, g_ui8Adjust * g_ui32Load / 1000);
PWMOutputState(PWM0_BASE, PWM_OUT_0_BIT, true);
PWMGenEnable(PWM0_BASE, PWM_GEN_0);
// usb
g_bUSBConfigured = false;
USBBufferInit(&g_sTxBuffer);
USBBufferInit(&g_sRxBuffer);
USBStackModeSet(0, eUSBModeForceDevice, 0);
USBDBulkInit(0, &g_sBulkDevice);
// dma
SysCtlPeripheralClockGating(true);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UDMA);
IntEnable(INT_UDMAERR);
uDMAEnable();
uDMAControlBaseSet(pui8ControlTable);
// adc
InitADC00();
IntMasterEnable();
UARTprintf("ADC USB\n");
tUSBRingBufObject sTxRing;
uint32_t ui32Space = 0, ui32WriteIndex = 0, ui32Count = 0, ui32HeaderIndex = 0;
bool header_reserved = false;
bool overflow = false;
USBBufferInfoGet(&g_sTxBuffer, &sTxRing);
ui32WriteIndex = sTxRing.ui32WriteIndex % BULK_TX_BUFFER_SIZE;
ui32Space = USBBufferSpaceAvailable(&g_sTxBuffer);
UARTprintf("=============================\n");
UARTprintf("begin: wi:%d sp:%d.\n", ui32WriteIndex, ui32Space);
//int16_t debug = 1;
while(true)
{
int32_t i, j, r0, r1;
bool samples_added = false;
///////////////////////////////////////////////////////////////////////
// add header if necessary
if((header_reserved == false) && (ui32Space > BULK_BLOCK_SIZE))
{
header_reserved = true;
ui32Space -= BULK_BLOCK_SIZE;
ui32HeaderIndex = ui32WriteIndex;
ui32WriteIndex = (ui32WriteIndex + BULK_BLOCK_SIZE) % BULK_TX_BUFFER_SIZE;
}
///////////////////////////////////////////////////////////////////////
// adc 0
r0 = g_adcOutIndex0 / ADC_BUFFER_SIZE;
int offset =((FILTER_SIZE - (ADC_BUFFER_SIZE-1))/ADC_BUFFER_SIZE)+2;
r1 = (r0 + offset) % ADC_BUFFER_WINDOW_COUNT;
if((g_adcPingPongStage0[r0]==2) && (g_adcPingPongStage0[r1]==2))
{
if(ui32Space > (BULK_BLOCK_SIZE * ADC_DECIMATED_BUFFER_SIZE))
{
if((overflow == false) || (g_usb_bytes_to_read > 0))
{
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 4);
for(j=ADC_DECIMATED_BUFFER_SIZE;j>0;j--)
{
// filter
int32_t accum[NUM_CHANNELS] = { 0 };
int32_t *filter = g_filter;
int32_t *src = &g_adcPingPong0[g_adcOutIndex0][0];
int32_t count = min(FILTER_SIZE, (ADC_BUFFER_SIZE*ADC_BUFFER_WINDOW_COUNT - g_adcOutIndex0));
for(i=count;i>0;i--)
{
int32_t f = *filter;
accum[0] += *src++ * f;
accum[1] += *src++ * f;
accum[2] += *src++ * f;
accum[3] += *src++ * f;
accum[4] += *src++ * f;
accum[5] += *src++ * f;
accum[6] += *src++ * f;
accum[7] += *src++ * f;
filter++;
}
int32_t next_count = FILTER_SIZE - count;
src = &g_adcPingPong0[0][0];
for(i=next_count;i>0;i--)
{
int32_t f = *filter;
accum[0] += *src++ * f;
accum[1] += *src++ * f;
accum[2] += *src++ * f;
accum[3] += *src++ * f;
accum[4] += *src++ * f;
accum[5] += *src++ * f;
accum[6] += *src++ * f;
accum[7] += *src++ * f;
filter++;
}
// add to usb buffer
int16_t *usb_ptr = (int16_t *)&g_pui8USBTxBuffer[ui32WriteIndex];
*usb_ptr++ = (accum[0] >> 16);
*usb_ptr++ = (accum[1] >> 16);
*usb_ptr++ = (accum[2] >> 16);
*usb_ptr++ = (accum[3] >> 16);
*usb_ptr++ = (accum[4] >> 16);
*usb_ptr++ = (accum[5] >> 16);
*usb_ptr++ = (accum[6] >> 16);
*usb_ptr++ = (accum[7] >> 16);
ui32Count += BULK_BLOCK_SIZE;
ui32Space -= BULK_BLOCK_SIZE;
ui32WriteIndex = (ui32WriteIndex + BULK_BLOCK_SIZE) % BULK_TX_BUFFER_SIZE;
g_adcOutIndex0 = (g_adcOutIndex0 + DECIMATION_INC) % (ADC_BUFFER_SIZE*ADC_BUFFER_WINDOW_COUNT);
}
samples_added = true;
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, 0);
}
}
else
{
// run out of space, reset
header_reserved = false;
USBBufferFlush(&g_sTxBuffer);
USBBufferInfoGet(&g_sTxBuffer, &sTxRing);
ui32Count = 0;
ui32WriteIndex = sTxRing.ui32WriteIndex % BULK_TX_BUFFER_SIZE;
ui32Space = USBBufferSpaceAvailable(&g_sTxBuffer);
header_reserved = false;
overflow = true;
UARTprintf("overflow sp:%d\n", ui32Space);
}
if(samples_added == false)
{
g_adcOutIndex0 = (g_adcOutIndex0 + ADC_BUFFER_SIZE) % (ADC_BUFFER_SIZE*ADC_BUFFER_WINDOW_COUNT);
}
g_adcPingPongStage0[r0] = 0;
if(g_ui8AdjustPending != g_ui8Adjust)
{
if(g_ui8AdjustPending < g_ui8Adjust)
{
g_ui8Adjust--;
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0, g_ui8Adjust * g_ui32Load / 1000);
}
else if(g_ui8AdjustPending > g_ui8Adjust)
{
g_ui8Adjust++;
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0, g_ui8Adjust * g_ui32Load / 1000);
}
}
GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_7, g_fire ? GPIO_PIN_7 : 0);
}
else
{
for(j=0;j<ADC_BUFFER_WINDOW_COUNT;j++)
{
if((g_adcPingPongStage0[j]==1) && (g_adcPingPongStage1[j]==1))
{
int32_t *src0 = &g_adcPingPong0[j*ADC_BUFFER_SIZE][0];
int32_t *src1 = &g_adcPingPong1[j*ADC_BUFFER_SIZE][0];
for(i=ADC_BUFFER_SIZE;i>0;i--)
{
*src0 += *src1++ - 0x1000; g_channelSum[0] -= *src0; *src0++ += g_channelOffset[0];
*src0 += *src1++ - 0x1000; g_channelSum[1] -= *src0; *src0++ += g_channelOffset[1];
*src0 += *src1++ - 0x1000; g_channelSum[2] -= *src0; *src0++ += g_channelOffset[2];
*src0 += *src1++ - 0x1000; g_channelSum[3] -= *src0; *src0++ += g_channelOffset[3];
*src0 += *src1++ - 0x1000; g_channelSum[4] -= *src0; *src0++ += g_channelOffset[4];
*src0 += *src1++ - 0x1000; g_channelSum[5] -= *src0; *src0++ += g_channelOffset[5];
*src0 += *src1++ - 0x1000; g_channelSum[6] -= *src0; *src0++ += g_channelOffset[6];
*src0 += *src1++ - 0x1000; g_channelSum[7] -= *src0; *src0++ += g_channelOffset[7];
}
g_adcPingPongStage0[j] = 2;
g_adcPingPongStage1[j] = 0;
g_channelOffsetCount++;
}
}
if(g_channelOffsetCount > CHANNEL_OFFSET_AVERAGE_COUNT)
{
g_channelOffset[0] = (g_channelOffset[0] / 2) + (int16_t)(g_channelSum[0] / CHANNEL_OFFSET_AVERAGE_DIVISOR);
g_channelOffset[1] = (g_channelOffset[1] / 2) + (int16_t)(g_channelSum[1] / CHANNEL_OFFSET_AVERAGE_DIVISOR);
g_channelOffset[2] = (g_channelOffset[2] / 2) + (int16_t)(g_channelSum[2] / CHANNEL_OFFSET_AVERAGE_DIVISOR);
g_channelOffset[3] = (g_channelOffset[3] / 2) + (int16_t)(g_channelSum[3] / CHANNEL_OFFSET_AVERAGE_DIVISOR);
g_channelOffset[4] = (g_channelOffset[4] / 2) + (int16_t)(g_channelSum[4] / CHANNEL_OFFSET_AVERAGE_DIVISOR);
g_channelOffset[5] = (g_channelOffset[5] / 2) + (int16_t)(g_channelSum[5] / CHANNEL_OFFSET_AVERAGE_DIVISOR);
g_channelOffset[6] = (g_channelOffset[6] / 2) + (int16_t)(g_channelSum[6] / CHANNEL_OFFSET_AVERAGE_DIVISOR);
g_channelOffset[7] = (g_channelOffset[7] / 2) + (int16_t)(g_channelSum[7] / CHANNEL_OFFSET_AVERAGE_DIVISOR);
g_channelOffsetCount = 0;
g_channelSum[0] = 0;
g_channelSum[1] = 0;
g_channelSum[2] = 0;
g_channelSum[3] = 0;
g_channelSum[4] = 0;
g_channelSum[5] = 0;
g_channelSum[6] = 0;
g_channelSum[7] = 0;
}
}
///////////////////////////////////////////////////////////////////////
// process USB if necessary
if(samples_added)
{
// flush usb buffer..
int32_t usb_want = g_usb_bytes_to_read - BULK_BLOCK_SIZE;
int32_t high_water = (USB_HIGH_WATER_MULTIPLIER * BULK_BLOCK_SIZE);
int32_t target_size = min(usb_want, high_water);
if((ui32Count == target_size) && (target_size > 0))
{
// add in header
int8_t *usb_ptr = (int8_t *)&g_pui8USBTxBuffer[ui32HeaderIndex];
// 16 byte header
*usb_ptr++ = 0xAA;
*usb_ptr++ = 0xAA;
*usb_ptr++ = (target_size >> 8) & 0xFF;
*usb_ptr++ = (target_size) & 0xFF;
for(i=12;i>0;i--)
*usb_ptr++ = 0;
USBBufferDataWritten(&g_sTxBuffer, target_size + BULK_BLOCK_SIZE);
header_reserved = false;
overflow = false;
IntMasterDisable();
g_usb_bytes_to_read -= (target_size + BULK_BLOCK_SIZE);
if(g_usb_bytes_to_read < 0)
g_usb_bytes_to_read = 0;
IntMasterEnable();
// get usb status
USBBufferInfoGet(&g_sTxBuffer, &sTxRing);
ui32WriteIndex = sTxRing.ui32WriteIndex % BULK_TX_BUFFER_SIZE;
ui32Space = USBBufferSpaceAvailable(&g_sTxBuffer);
ui32Count = 0;
}
}
else
{
if(g_stall0 > 0)
{
UARTprintf("stall 0:%d\n", g_stall0);
g_stall0 = 0;
}
if(g_stall1 > 0)
{
UARTprintf("stall 1:%d\n", g_stall1);
g_stall1 = 0;
}
}
}
}
void Timer0IntHandler(void)
{
// Clear the timer interrupt
TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
// Read the current state of the GPIO pin and
// write back the opposite state
/*
if(GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_2))
{
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, 0);
}
else
{
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 4);
}
*/
}
void uDMAErrorHandler(void)
{
uint32_t ui32Status;
ui32Status = uDMAErrorStatusGet();
if(ui32Status)
{
uDMAErrorStatusClear();
g_ui32DMAErrCount++;
}
}
// ADC interrupt handler. Called on completion of uDMA transfer
void ADC00IntHandler(void)
{
uint32_t ui32ModeP;
uint32_t ui32ModeA;
ADCIntClear(ADC0_BASE, 0);
ui32ModeP = uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT);
if(ui32ModeP == UDMA_MODE_STOP)
{
uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT,
UDMA_MODE_PINGPONG,
(void *) (ADC0_BASE + ADC_O_SSFIFO0),
&g_adcPingPong0[g_adcInIndex0][0], NUM_CHANNELS);
uDMAChannelEnable(UDMA_CHANNEL_ADC0);
g_adcInIndex0 = (g_adcInIndex0 + 1) % (ADC_BUFFER_SIZE*ADC_BUFFER_WINDOW_COUNT);
if((g_adcInIndex0 % ADC_BUFFER_SIZE) == 2)
{
int32_t ready_buffer = (g_adcInIndex0 / ADC_BUFFER_SIZE);
if(g_adcPingPongStage0[ready_buffer % ADC_BUFFER_WINDOW_COUNT] != 0)
g_stall0++;
ready_buffer = (ready_buffer - 1 + ADC_BUFFER_WINDOW_COUNT) % ADC_BUFFER_WINDOW_COUNT;
g_adcPingPongStage0[ready_buffer] = 1;
}
}
else
{
ui32ModeA = uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT);
if(ui32ModeA == UDMA_MODE_STOP)
{
uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT,
UDMA_MODE_PINGPONG,
(void *) (ADC0_BASE + ADC_O_SSFIFO0),
&g_adcPingPong0[g_adcInIndex0][0], NUM_CHANNELS);
uDMAChannelEnable(UDMA_CHANNEL_ADC0);
g_adcInIndex0 = (g_adcInIndex0 + 1) % (ADC_BUFFER_SIZE*ADC_BUFFER_WINDOW_COUNT);
if((g_adcInIndex0 % ADC_BUFFER_SIZE) == 2)
{
int32_t ready_buffer = (g_adcInIndex0 / ADC_BUFFER_SIZE);
if(g_adcPingPongStage0[ready_buffer % ADC_BUFFER_WINDOW_COUNT] != 0)
g_stall0++;
ready_buffer = (ready_buffer - 1 + ADC_BUFFER_WINDOW_COUNT) % ADC_BUFFER_WINDOW_COUNT;
g_adcPingPongStage0[ready_buffer] = 1;
}
}
}
}
// ADC interrupt handler. Called on completion of uDMA transfer
void ADC10IntHandler(void)
{
uint32_t ui32ModeP;
uint32_t ui32ModeA;
ADCIntClear(ADC1_BASE, 0);
ui32ModeP = uDMAChannelModeGet(UDMA_CHANNEL_SSI1RX | UDMA_PRI_SELECT);
if(ui32ModeP == UDMA_MODE_STOP)
{
uDMAChannelTransferSet(UDMA_CHANNEL_SSI1RX | UDMA_PRI_SELECT,
UDMA_MODE_PINGPONG,
(void *) (ADC1_BASE + ADC_O_SSFIFO0),
&g_adcPingPong1[g_adcInIndex1][0], NUM_CHANNELS);
uDMAChannelEnable(UDMA_CHANNEL_SSI1RX);
g_adcInIndex1 = (g_adcInIndex1 + 1) % (ADC_BUFFER_SIZE*ADC_BUFFER_WINDOW_COUNT);
if((g_adcInIndex1 % ADC_BUFFER_SIZE) == 2)
{
int32_t ready_buffer = (g_adcInIndex1 / ADC_BUFFER_SIZE);
if(g_adcPingPongStage1[ready_buffer % ADC_BUFFER_WINDOW_COUNT] != 0)
g_stall1++;
ready_buffer = (ready_buffer - 1 + ADC_BUFFER_WINDOW_COUNT) % ADC_BUFFER_WINDOW_COUNT;
g_adcPingPongStage1[ready_buffer] = 1;
}
}
else
{
ui32ModeA = uDMAChannelModeGet(UDMA_CHANNEL_SSI1RX | UDMA_ALT_SELECT);
if(ui32ModeA == UDMA_MODE_STOP)
{
uDMAChannelTransferSet(UDMA_CHANNEL_SSI1RX | UDMA_ALT_SELECT,
UDMA_MODE_PINGPONG,
(void *) (ADC1_BASE + ADC_O_SSFIFO0),
&g_adcPingPong1[g_adcInIndex1][0], NUM_CHANNELS);
uDMAChannelEnable(UDMA_CHANNEL_SSI1RX);
g_adcInIndex1 = (g_adcInIndex1 + 1) % (ADC_BUFFER_SIZE*ADC_BUFFER_WINDOW_COUNT);
if((g_adcInIndex1 % ADC_BUFFER_SIZE) == 2)
{
int32_t ready_buffer = (g_adcInIndex1 / ADC_BUFFER_SIZE);
if(g_adcPingPongStage1[ready_buffer % ADC_BUFFER_WINDOW_COUNT] != 0)
g_stall1++;
ready_buffer = (ready_buffer - 1 + ADC_BUFFER_WINDOW_COUNT) % ADC_BUFFER_WINDOW_COUNT;
g_adcPingPongStage1[ready_buffer] = 1;
}
}
}
}
// Initialize ADC 0 0 uDMA transfer
void InitADC00(void)
{
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC1);
GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3 | GPIO_PIN_2 | GPIO_PIN_1 | GPIO_PIN_0); // 0, 1, 2, 3
GPIOPinTypeADC(GPIO_PORTD_BASE, GPIO_PIN_3 | GPIO_PIN_2 | GPIO_PIN_1 | GPIO_PIN_0); // 4, 5, 6, 7
ADCHardwareOversampleConfigure(ADC0_BASE, HW_AVERAGE);
ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_ALWAYS, 0);
ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH0);
ADCSequenceStepConfigure(ADC0_BASE, 0, 1, ADC_CTL_CH1);
ADCSequenceStepConfigure(ADC0_BASE, 0, 2, ADC_CTL_TS);
ADCSequenceStepConfigure(ADC0_BASE, 0, 3, ADC_CTL_TS);
ADCSequenceStepConfigure(ADC0_BASE, 0, 4, ADC_CTL_TS);
ADCSequenceStepConfigure(ADC0_BASE, 0, 5, ADC_CTL_TS);
ADCSequenceStepConfigure(ADC0_BASE, 0, 6, ADC_CTL_TS);
ADCSequenceStepConfigure(ADC0_BASE, 0, 7, ADC_CTL_TS|ADC_CTL_IE|ADC_CTL_END);
ADCSequenceDMAEnable(ADC0_BASE, 0);
// adc 0
uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC0,
UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST |
UDMA_ATTR_HIGH_PRIORITY |
UDMA_ATTR_REQMASK);
uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT,
UDMA_SIZE_32 | UDMA_SRC_INC_NONE | UDMA_DST_INC_32 |
UDMA_ARB_8);
uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT,
UDMA_SIZE_32 | UDMA_SRC_INC_NONE | UDMA_DST_INC_32 |
UDMA_ARB_8);
uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT,
UDMA_MODE_PINGPONG,
(void *) (ADC0_BASE + ADC_O_SSFIFO0),
&g_adcPingPong0[0][0], NUM_CHANNELS);
uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT,
UDMA_MODE_PINGPONG,
(void *) (ADC0_BASE + ADC_O_SSFIFO0),
&g_adcPingPong0[1][0], NUM_CHANNELS);
g_adcInIndex0 = 2;
// adc 1
ADCHardwareOversampleConfigure(ADC1_BASE, HW_AVERAGE);
ADCSequenceConfigure(ADC1_BASE, 0, ADC_TRIGGER_ALWAYS, 1);
ADCSequenceStepConfigure(ADC1_BASE, 0, 0, ADC_CTL_CH0);
ADCSequenceStepConfigure(ADC1_BASE, 0, 1, ADC_CTL_CH1);
ADCSequenceStepConfigure(ADC1_BASE, 0, 2, ADC_CTL_TS);
ADCSequenceStepConfigure(ADC1_BASE, 0, 3, ADC_CTL_TS);
ADCSequenceStepConfigure(ADC1_BASE, 0, 4, ADC_CTL_TS);
ADCSequenceStepConfigure(ADC1_BASE, 0, 5, ADC_CTL_TS);
ADCSequenceStepConfigure(ADC1_BASE, 0, 6, ADC_CTL_TS);
ADCSequenceStepConfigure(ADC1_BASE, 0, 7, ADC_CTL_TS|ADC_CTL_IE|ADC_CTL_END);
ADCPhaseDelaySet(ADC1_BASE, ADC_PHASE_180);
ADCSequenceDMAEnable(ADC1_BASE, 0);
uDMAChannelAttributeDisable(UDMA_CHANNEL_SSI1RX,
UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST |
UDMA_ATTR_HIGH_PRIORITY |
UDMA_ATTR_REQMASK);
uDMAChannelControlSet(UDMA_CHANNEL_SSI1RX | UDMA_PRI_SELECT,
UDMA_SIZE_32 | UDMA_SRC_INC_NONE | UDMA_DST_INC_32 |
UDMA_ARB_8);
uDMAChannelControlSet(UDMA_CHANNEL_SSI1RX | UDMA_ALT_SELECT,
UDMA_SIZE_32 | UDMA_SRC_INC_NONE | UDMA_DST_INC_32 |
UDMA_ARB_8);
uDMAChannelTransferSet(UDMA_CHANNEL_SSI1RX | UDMA_PRI_SELECT,
UDMA_MODE_PINGPONG,
(void *) (ADC1_BASE + ADC_O_SSFIFO0),
&g_adcPingPong1[0][0], NUM_CHANNELS);
uDMAChannelTransferSet(UDMA_CHANNEL_SSI1RX | UDMA_ALT_SELECT,
UDMA_MODE_PINGPONG,
(void *) (ADC1_BASE + ADC_O_SSFIFO0),
&g_adcPingPong1[1][0], NUM_CHANNELS);
uDMAChannelAssign(UDMA_CH24_ADC1_0);
g_adcInIndex1 = 2;
// both
uDMAChannelEnable(UDMA_CHANNEL_ADC0);
uDMAChannelEnable(UDMA_CHANNEL_SSI1RX);
ADCIntClear(ADC0_BASE, 0);
ADCIntClear(ADC1_BASE, 0);
ADCIntEnable(ADC0_BASE, 0);
ADCIntEnable(ADC1_BASE, 0);
IntEnable(INT_ADC0SS0);
IntEnable(INT_ADC1SS0);
ADCSequenceEnable(ADC0_BASE, 0);
ADCSequenceEnable(ADC1_BASE, 0);
}
//*****************************************************************************
//
// Configure the UART and its pins. This must be called before UARTprintf().
//
//*****************************************************************************
void ConfigureUART(void)
{
//
// Enable the GPIO Peripheral used by the UART.
//
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
//
// Enable UART0
//
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
//
// Configure GPIO Pins for UART mode.
//
ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
//
// Use the internal 16MHz oscillator as the UART clock source.
//
UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
//
// Initialize the UART for console I/O.
//
UARTStdioConfig(0, 115200, 16000000);
}
//*****************************************************************************
//
// Handles bulk driver notifications related to the transmit channel (data to
// the USB host).
//
// \param pvCBData is the client-supplied callback pointer for this channel.
// \param ui32Event identifies the event we are being notified about.
// \param ui32MsgValue is an event-specific value.
// \param pvMsgData is an event-specific pointer.
//
// This function is called by the bulk driver to notify us of any events
// related to operation of the transmit data channel (the IN channel carrying
// data to the USB host).
//
// \return The return value is event-specific.
//
//*****************************************************************************
uint32_t
TxHandler(void *pvCBData, uint32_t ui32Event, uint32_t ui32MsgValue,
void *pvMsgData)
{
//
// We are not required to do anything in response to any transmit event
// in this example. All we do is update our transmit counter.
//
if(ui32Event == USB_EVENT_TX_COMPLETE)
{
//g_ui32TxCount += ui32MsgValue;
}
//
// Dump a debug message.
//
//DEBUG_PRINT("TX complete %d\n", ui32MsgValue);
return(0);
}
//*****************************************************************************
//
// Handles bulk driver notifications related to the receive channel (data from
// the USB host).
//
// \param pvCBData is the client-supplied callback pointer for this channel.
// \param ui32Event identifies the event we are being notified about.
// \param ui32MsgValue is an event-specific value.
// \param pvMsgData is an event-specific pointer.
//
// This function is called by the bulk driver to notify us of any events
// related to operation of the receive data channel (the OUT channel carrying
// data from the USB host).
//
// \return The return value is event-specific.
//
//*****************************************************************************
uint32_t
RxHandler(void *pvCBData, uint32_t ui32Event,
uint32_t ui32MsgValue, void *pvMsgData)
{
//
// Which event are we being sent?
//
switch(ui32Event)
{
//
// We are connected to a host and communication is now possible.
//
case USB_EVENT_CONNECTED:
{
g_bUSBConfigured = true;
UARTprintf("Host connected.\n");
//
// Flush our buffers.
//
USBBufferFlush(&g_sTxBuffer);
USBBufferFlush(&g_sRxBuffer);
IntMasterDisable();
g_usb_bytes_to_read = 0;
IntMasterEnable();
break;
}
//
// The host has disconnected.
//
case USB_EVENT_DISCONNECTED:
{
g_bUSBConfigured = false;
UARTprintf("Host disconnected.\n");
break;
}
//
// A new packet has been received.
//
case USB_EVENT_RX_AVAILABLE:
{
int i;
//
// Read the command 0xAA
//
// byte 0: command = 0xAA read
// byte 1: MSB buffer size
// byte 2: LSB buffer size
uint8_t *data = (uint8_t *)pvMsgData;
uint8_t pwm;
for(i = ui32MsgValue;i>0;i--)
{
switch(g_msg_index)
{
default:
case 0:
if(*data == 0xAA)
{
g_msg_index++;
}
break;
case 1:
if(*data == 0xAA)
g_msg_index++;
else
g_msg_index = 0;
break;
case 2:
g_usb_receive_size = (uint32_t)*data << 8;
g_msg_index++;
break;
case 3:
g_usb_receive_size += (uint32_t)*data;
if(g_usb_receive_size > 0)
{
IntMasterDisable();
g_usb_bytes_to_read += g_usb_receive_size;
IntMasterEnable();
}
g_msg_index++;
break;
case 4:
pwm = *data;
pwm = min(pwm, PWM_MAX);
pwm = max(pwm, PWM_MIN);
g_ui8AdjustPending = pwm;
//if(g_ui8AdjustPending != g_ui8Adjust)
//{
// cause stack overflow
//UARTprintf("pwm:%d\n", g_ui8AdjustPending);
//}
g_msg_index++;
break;
case 5:
g_fire = (*data);
g_msg_index = 0;
break;
}
data++;
if(data >= &g_pui8USBRxBuffer[BULK_RX_BUFFER_SIZE])
{
data = &g_pui8USBRxBuffer[0];
}
}
USBBufferDataRemoved(&g_sRxBuffer, ui32MsgValue);
break;
}
//
// Ignore SUSPEND and RESUME for now.
//
case USB_EVENT_SUSPEND:
case USB_EVENT_RESUME:
{
break;
}
//
// Ignore all other events and return 0.
//
default:
{
break;
}
}
return(0);
}
Comments