ECE3849/main.c
2025-04-24 12:07:27 -04:00

367 lines
11 KiB
C

/*
* ECE 3849 Lab2 starter project
*
* Gene Bogdanov 9/13/2017
*/
/* XDCtools Header files */
#include <xdc/std.h>
#include <xdc/runtime/System.h>
#include <xdc/cfg/global.h>
/* BIOS Header files */
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/knl/Semaphore.h>
#include <stdint.h>
#include <stdbool.h>
#include "driverlib/fpu.h"
#include "driverlib/sysctl.h"
#include "driverlib/interrupt.h"
#include "driverlib/timer.h"
#include "Crystalfontz128x128_ST7735.h"
#include <stdio.h>
#include "grlib/grlib.h"
#include <math.h>
#include <string.h>
#include "inc/hw_memmap.h"
#include "driverlib/gpio.h"
#include "driverlib/pwm.h"
#include "driverlib/pin_map.h"
#include "sampling.h"
#include "buttons.h"
#include "frequency.h"
#include "kiss_fft.h"
#include "_kiss_fft_guts.h"
#define PI 3.14159265358979f
tContext sContext;
uint32_t cputime_unloaded;
#define FFT_EN false
#define LOCAL_BUF_LEN 128
uint16_t adc_buffer_sample[LOCAL_BUF_LEN]; // copy of g adc buffer
uint8_t adc_buffer_processed[LOCAL_BUF_LEN]; // copy of g adc buffer
#define FFT_BUF_LEN 1024
uint16_t adc_buffer_fft_sample[FFT_BUF_LEN]; // copy of g adc buffer
uint8_t adc_buffer_fft_processed[FFT_BUF_LEN]; // copy of g adc buffer
kiss_fft_cfg cfg;
static kiss_fft_cpx in[FFT_BUF_LEN], out[FFT_BUF_LEN];
typedef struct
{
bool fft;
uint8_t voltage_scale;
uint8_t time_scale;
uint8_t trigger_mode;
} Options;
Options options = {
.fft = false, // oscilloscope mode
.voltage_scale = 4, // 2v
.time_scale = 5, // 20us
.trigger_mode = 1, // rising
};
// assuming square lcd
#define HEIGHT LCD_VERTICAL_MAX
#define WIDTH LCD_HORIZONTAL_MAX
#define PIXELS_PER_DIV 20
#define VIN_RANGE 3.3 // volts
#define ADC_BITS 12
#define ADC_OFFSET 100
#define VOLTAGE_SCALES 5
const char * const gVoltageScaleStr[VOLTAGE_SCALES] = {
"100 mV", "200 mV", "500 mV", " 1 V", " 2 V"
};
const float gVoltageScale[VOLTAGE_SCALES] = {
0.1, 0.2, 0.5, 1., 2.
};
int Trigger(bool rising);
uint32_t cpu_load_count(void)
{
uint32_t i = 0;
TimerIntClear(TIMER1_BASE, TIMER_TIMA_TIMEOUT);
TimerEnable(TIMER1_BASE, TIMER_A); // start one-shot timer
while (!(TimerIntStatus(TIMER1_BASE, false) & TIMER_TIMA_TIMEOUT))
i++;
return i;
}
void start_cputimer() {
// initialize timer 1 in one-shot mode for polled timing
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);
TimerDisable(TIMER1_BASE, TIMER_BOTH);
TimerConfigure(TIMER1_BASE, TIMER_CFG_ONE_SHOT);
TimerLoadSet(TIMER1_BASE, TIMER_A, gSystemClock/100); // 10ms interval
// baseline load
cputime_unloaded = cpu_load_count();
}
/*
* ======== main ========
*/
int main(void)
{
IntMasterDisable();
// hardware initialization goes here
start_signal();
start_sampler();
ButtonInit();
start_cputimer();
start_frequency_scan();
Crystalfontz128x128_Init(); // Initialize the LCD display driver
Crystalfontz128x128_SetOrientation(LCD_ORIENTATION_UP); // set screen orientation
GrContextInit(&sContext, &g_sCrystalfontz128x128); // Initialize the grlib graphics context
GrContextFontSet(&sContext, &g_sFontFixed6x8); // select font
#define KISS_FFT_CFG_SIZE (sizeof(struct kiss_fft_state)+sizeof(kiss_fft_cpx)*(FFT_BUF_LEN-1))
static char kiss_fft_cfg_buffer[KISS_FFT_CFG_SIZE];
size_t buffer_size = KISS_FFT_CFG_SIZE;
cfg = kiss_fft_alloc(FFT_BUF_LEN, 0, kiss_fft_cfg_buffer, &buffer_size);
/* Start BIOS */
BIOS_start();
return (0);
}
void capture_waveform(UArg arg1, UArg arg2)
{
IntMasterEnable();
while(true) {
Semaphore_pend(capture_sem, BIOS_WAIT_FOREVER);
int32_t adc_current_index;
int32_t i;
if (options.fft) {
adc_current_index = getADCBufferIndex();
int32_t j = 0;
for(i=adc_current_index-FFT_BUF_LEN; i<adc_current_index; i++) {
adc_buffer_fft_sample[j++] = gADCBuffer[ADC_BUFFER_WRAP(adc_current_index + i)];
}
} else {
// single read of options is atomic
int32_t trigger_mode = options.trigger_mode;
int trigger;
if (trigger_mode == 2) {
trigger = getADCBufferIndex() - (WIDTH / 2); // show latest if trigger disabled
} else {
trigger = Trigger(trigger_mode);
}
adc_current_index = trigger - (WIDTH / 2);
for (i=0; i<LOCAL_BUF_LEN; i++) {
adc_buffer_sample[i] = gADCBuffer[ADC_BUFFER_WRAP(adc_current_index + i)];
}
}
Semaphore_post(process_sem);
}
}
void process_waveform(UArg arg1, UArg arg2) {
static float w[FFT_BUF_LEN];
int i;
for (i=0; i<FFT_BUF_LEN; i++) {
w[i] = 0.42f
- 0.5f * cosf(2*PI*i/(FFT_BUF_LEN-1))
+ 0.08f * cosf(4*PI*i/(FFT_BUF_LEN-1));
}
while(true) {
Semaphore_pend(process_sem, BIOS_WAIT_FOREVER);
int i;
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
#define CONSTRAIN(x) MAX(MIN(HEIGHT - 1, x), 0)
if(options.fft) { // fft
for(i=0; i<FFT_BUF_LEN; i++) {
//in[i].r = sinf(20*PI*i/FFT_BUF_LEN);
in[i].r = (float) adc_buffer_fft_sample[i] * w[i];
in[i].i = 0;
}
kiss_fft(cfg, in, out);
for(i=0; i<LOCAL_BUF_LEN; i++) {
#define DB(out) 10 * log10f(out.r * out.r + out.i * out.i)
//adc_buffer_processed[i] = CONSTRAIN(HEIGHT- (int)roundf(0.5 * DB(out[i])));
adc_buffer_processed[i] = CONSTRAIN(HEIGHT- (int) (DB(out[i])) );
}
} else {
// single read of global options is atomic
float fVoltsPerDiv = gVoltageScale[options.voltage_scale];
float fScale = (VIN_RANGE * PIXELS_PER_DIV)/((1 << ADC_BITS) * fVoltsPerDiv);
for(i=0; i<LOCAL_BUF_LEN; i++) {
#define TRANSPOSE(x) CONSTRAIN((HEIGHT/2) - (int)roundf(fScale * ((int)x - ADC_OFFSET)))
adc_buffer_processed[i] = TRANSPOSE(adc_buffer_sample[i]);
}
}
Semaphore_post(display_sem);
Semaphore_post(capture_sem);
}
}
void handle_user_input() {
// handle buttons
Button button = (Button) 0;
Options local_options = options; // this is the only writer
while (true) {
Mailbox_pend(button_mailbox, &button, BIOS_WAIT_FOREVER);
switch (button) {
case S1: // toggle edge
local_options.trigger_mode = (local_options.trigger_mode + 1) % 3;
break;
case S2: // toggle fft
local_options.fft ^= 1;
break;
case Up: // next scale
local_options.voltage_scale = (local_options.voltage_scale + 1) % VOLTAGE_SCALES;
break;
case Down: // previous scale
local_options.voltage_scale = (local_options.voltage_scale + VOLTAGE_SCALES - 1) % VOLTAGE_SCALES;
break;
case SW1:
set_pwm_period(++gPWMPeriod);
break;
case SW2:
set_pwm_period(--gPWMPeriod);
break;
}
Semaphore_pend(options_sem, BIOS_WAIT_FOREVER);
options = local_options;
Semaphore_post(options_sem);
}
}
void display_waveform(UArg arg1, UArg arg2)
{
char str[50]; // string buffer
tRectangle rectFullScreen = {0, 0, GrContextDpyWidthGet(&sContext)-1, GrContextDpyHeightGet(&sContext)-1};
while(1) {
Semaphore_pend(display_sem, BIOS_WAIT_FOREVER);
Semaphore_pend(options_sem, BIOS_WAIT_FOREVER);
Options local_options = options;
Semaphore_post(options_sem);
// calculate cpu usage
uint32_t cputime_loaded;
cputime_loaded = cpu_load_count();
float usage_percent;
usage_percent = (1.0 - (float) cputime_loaded / (float) cputime_unloaded) * (float) 100.;
GrContextForegroundSet(&sContext, ClrBlack);
GrRectFill(&sContext, &rectFullScreen); // fill screen with black
GrContextForegroundSet(&sContext, ClrBlue);
// draw gridlines from the center out
uint8_t xy_pos;
for (xy_pos = HEIGHT/2; xy_pos < HEIGHT; xy_pos += PIXELS_PER_DIV) {
GrLineDrawV(&sContext, xy_pos, 0, 128); // right
GrLineDrawV(&sContext, HEIGHT - xy_pos, 0, 128); // left
GrLineDrawH(&sContext, 0, 128, xy_pos); // down
GrLineDrawH(&sContext, 0, 128, HEIGHT - xy_pos); // up
}
// info
GrContextForegroundSet(&sContext, ClrWheat);
snprintf(str, sizeof(str), "CPU Load %.1f%%", usage_percent);
GrStringDraw(&sContext, str, /*length*/ -1, /*x*/ 0, /*y*/ HEIGHT - 30, /*opaque*/ false);
uint32_t period = gPeriod;
float frequency = (float) gSystemClock / (float) period;
snprintf(str, sizeof(str), "f = %.1fhz", frequency);
GrStringDraw(&sContext, str, /*length*/ -1, /*x*/ 0, /*y*/ HEIGHT - 10, /*opaque*/ false);
snprintf(str, sizeof(str), "T = %dc", period);
GrStringDraw(&sContext, str, /*length*/ -1, /*x*/ 0, /*y*/ HEIGHT - 20, /*opaque*/ false);
if (local_options.fft) {
GrStringDraw(&sContext, "20 dB" , /*length*/ -1, /*x*/ 0, /*y*/ 0, /*opaque*/ false);
} else {
GrStringDraw(&sContext, gVoltageScaleStr[local_options.voltage_scale], /*length*/ -1, /*x*/ 0, /*y*/ 0, /*opaque*/ false);
switch (local_options.trigger_mode) {
case 1:
GrStringDraw(&sContext, "^", /*length*/ -1, /*x*/ WIDTH - 10, /*y*/ 0, /*opaque*/ false);
break;
case 0:
GrStringDraw(&sContext, "v", /*length*/ -1, /*x*/ WIDTH - 10, /*y*/ 0, /*opaque*/ false);
break;
case 2:
GrStringDraw(&sContext, "-", /*length*/ -1, /*x*/ WIDTH - 10, /*y*/ 0, /*opaque*/ false);
break;
}
}
// display graph
GrContextForegroundSet(&sContext, ClrYellow);
int j;
for(j=1; j<LOCAL_BUF_LEN; j++) {
GrLineDraw(&sContext, j-1, adc_buffer_processed[j-1], j, adc_buffer_processed[j]);
}
GrFlush(&sContext); // flush the frame buffer to the LCD
}
}
int Trigger(bool rising) // search for edge trigger
{
int x = getADCBufferIndex() - (WIDTH / 2); // half screen width
int x_stop = x - ADC_BUFFER_SIZE/2;
for (; x > x_stop; x--) {
if (rising) {
if ( gADCBuffer[ADC_BUFFER_WRAP(x)] >= ADC_OFFSET &&
gADCBuffer[ADC_BUFFER_WRAP(x-1)] < ADC_OFFSET) {
break;
}
} else { // falling edge trigger
if ( gADCBuffer[ADC_BUFFER_WRAP(x)] <= ADC_OFFSET &&
gADCBuffer[ADC_BUFFER_WRAP(x-1)] > ADC_OFFSET) {
break;
}
}
}
if (x == x_stop) // for loop ran to the end
x = getADCBufferIndex() - (WIDTH / 2); // reset x back to how it was initialized
return x;
}