331 lines
9.9 KiB
C
331 lines
9.9 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"
|
|
#define PWM_FREQUENCY 20000 // PWM frequency = 20 kHz
|
|
|
|
uint32_t gSystemClock = 120000000; // [Hz] system clock frequency
|
|
|
|
tContext sContext;
|
|
|
|
uint32_t cputime_unloaded;
|
|
|
|
#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
|
|
|
|
uint8_t voltage_scale = 4; // 2v
|
|
uint8_t time_scale = 5; // 20us
|
|
uint8_t 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 30
|
|
|
|
#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.
|
|
};
|
|
|
|
#define TIME_SCALES 6
|
|
const char * const gTimeScaleStr[TIME_SCALES] = {
|
|
"100 ms", "50 ms", "20 ms", " 10 ms", "50 us", "20 us"
|
|
};
|
|
|
|
const uint64_t gTImeScale[TIME_SCALES] = {
|
|
100 * 1000 / PIXELS_PER_DIV,
|
|
50 * 1000 / PIXELS_PER_DIV,
|
|
20 * 1000 / PIXELS_PER_DIV,
|
|
10 * 1000 / PIXELS_PER_DIV,
|
|
50 / PIXELS_PER_DIV,
|
|
20 / PIXELS_PER_DIV,
|
|
};
|
|
|
|
int Trigger(bool rising);
|
|
|
|
// start a pwm test signal
|
|
void start_signal() {
|
|
// configure M0PWM2, at GPIO PF2, BoosterPack 1 header C1 pin 2
|
|
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
|
|
GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_2);
|
|
GPIOPinConfigure(GPIO_PF2_M0PWM2);
|
|
GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_STRENGTH_2MA,
|
|
GPIO_PIN_TYPE_STD);
|
|
// configure the PWM0 peripheral, gen 1, outputs 2 and 3
|
|
SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);
|
|
// use system clock without division
|
|
PWMClockSet(PWM0_BASE, PWM_SYSCLK_DIV_1);
|
|
PWMGenConfigure(PWM0_BASE, PWM_GEN_1,
|
|
PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);
|
|
PWMGenPeriodSet(PWM0_BASE, PWM_GEN_1,
|
|
roundf((float)gSystemClock / PWM_FREQUENCY));
|
|
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_2,
|
|
roundf((float)gSystemClock / PWM_FREQUENCY * 0.4f));
|
|
PWMOutputState(PWM0_BASE, PWM_OUT_2_BIT, true);
|
|
PWMGenEnable(PWM0_BASE, PWM_GEN_1);
|
|
}
|
|
|
|
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();
|
|
|
|
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
|
|
|
|
/* Start BIOS */
|
|
BIOS_start();
|
|
|
|
return (0);
|
|
}
|
|
|
|
void task0_func(UArg arg1, UArg arg2)
|
|
{
|
|
IntMasterEnable();
|
|
|
|
while (true) {
|
|
// do nothing
|
|
}
|
|
}
|
|
|
|
void capture_waveform(UArg arg1, UArg arg2)
|
|
{
|
|
IntMasterEnable();
|
|
while(true) {
|
|
Semaphore_pend(capture_sem, BIOS_WAIT_FOREVER);
|
|
|
|
int32_t adc_current_index;
|
|
int32_t j;
|
|
int trigger;
|
|
if (trigger_mode == 2) {
|
|
trigger = gADCBufferIndex - (WIDTH / 2); // show latest if trigger disabled
|
|
} else {
|
|
trigger = Trigger(trigger_mode);
|
|
}
|
|
|
|
adc_current_index = trigger - (WIDTH / 2);
|
|
for (j=0; j<LOCAL_BUF_LEN; j++) {
|
|
adc_buffer_sample[j] = gADCBuffer[ADC_BUFFER_WRAP(adc_current_index + j)];
|
|
}
|
|
|
|
Semaphore_post(process_sem);
|
|
}
|
|
}
|
|
|
|
void process_waveform(UArg arg1, UArg arg2) {
|
|
while(true) {
|
|
Semaphore_pend(process_sem, BIOS_WAIT_FOREVER);
|
|
|
|
float fVoltsPerDiv = gVoltageScale[voltage_scale];
|
|
float fScale = (VIN_RANGE * PIXELS_PER_DIV)/((1 << ADC_BITS) * fVoltsPerDiv);
|
|
|
|
int j;
|
|
for(j=0; j<LOCAL_BUF_LEN; j++) {
|
|
#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)
|
|
#define TRANSPOSE(x) CONSTRAIN((HEIGHT/2) - (int)roundf(fScale * ((int)x - ADC_OFFSET)))
|
|
adc_buffer_processed[j] = TRANSPOSE(adc_buffer_sample[j]);
|
|
}
|
|
|
|
Semaphore_post(display_sem);
|
|
Semaphore_post(capture_sem);
|
|
}
|
|
}
|
|
|
|
void handle_user_input() {
|
|
// handle buttons
|
|
Button button = (Button) 0;
|
|
while (true) {
|
|
Mailbox_pend(button_mailbox, &button, BIOS_WAIT_FOREVER);
|
|
|
|
switch (button) {
|
|
case S1: // toggle edge
|
|
trigger_mode = (trigger_mode + 1) % 3;
|
|
break;
|
|
case Up: // next scale
|
|
voltage_scale = (voltage_scale + 1) % VOLTAGE_SCALES;
|
|
break;
|
|
case Down: // previous scale
|
|
voltage_scale = (voltage_scale + VOLTAGE_SCALES - 1) % VOLTAGE_SCALES;
|
|
break;
|
|
case Right: // next scale
|
|
time_scale = (time_scale + 1) % TIME_SCALES;
|
|
set_frequency(gTImeScale[time_scale]);
|
|
break;
|
|
case Left: // previous scale
|
|
time_scale = (time_scale + TIME_SCALES - 1) % TIME_SCALES;
|
|
set_frequency(gTImeScale[time_scale]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
// 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);
|
|
GrStringDraw(&sContext, gVoltageScaleStr[voltage_scale], /*length*/ -1, /*x*/ 0, /*y*/ 0, /*opaque*/ false);
|
|
GrStringDraw(&sContext, gTimeScaleStr[time_scale], /*length*/ -1, /*x*/ 60, /*y*/ 0, /*opaque*/ false);
|
|
snprintf(str, sizeof(str), "CPU Load %.1f%%", usage_percent);
|
|
GrStringDraw(&sContext, str, /*length*/ -1, /*x*/ 0, /*y*/ HEIGHT - 10, /*opaque*/ false);
|
|
|
|
switch (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=0; j<LOCAL_BUF_LEN; j++) {
|
|
#define MIN(a,b) (((a)<(b))?(a):(b))
|
|
#define MAX(a,b) (((a)>(b))?(a):(b))
|
|
uint32_t upper,lower, current, last;
|
|
|
|
if (j==0) {
|
|
upper = adc_buffer_processed[j];
|
|
lower = upper;
|
|
last = upper;
|
|
} else {
|
|
current = adc_buffer_processed[j];
|
|
|
|
upper = MAX(current, last);
|
|
lower = MIN(current, last);
|
|
|
|
last = current;
|
|
}
|
|
|
|
GrLineDrawV(&sContext, j, lower, upper);
|
|
}
|
|
|
|
GrFlush(&sContext); // flush the frame buffer to the LCD
|
|
|
|
}
|
|
}
|
|
|
|
|
|
int Trigger(bool rising) // search for edge trigger
|
|
{
|
|
int x = gADCBufferIndex - (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 = gADCBufferIndex - (WIDTH / 2); // reset x back to how it was initialized
|
|
return x;
|
|
}
|