Compare commits

..

7 commits

5 changed files with 257 additions and 15 deletions

View file

@ -28,7 +28,6 @@ uint32_t gADCSamplingRate; // [Hz] actual ADC sampling rate
// imported globals
extern uint32_t gSystemClock; // [Hz] system clock frequency
extern volatile uint32_t gTime; // time in hundredths of a second
extern volatile uint8_t drawRequested;
// initialize all button and joystick handling hardware
void ButtonInit(void)
@ -182,6 +181,7 @@ void ButtonISR(void) {
ButtonReadJoystick(); // Convert joystick state to button presses. The result is in gButtons.
uint32_t presses = ~old_buttons & gButtons; // detect button presses (transitions from not pressed to pressed)
presses |= ButtonAutoRepeat(); // autorepeat presses if a button is held long enough
#ifdef DISPLAY_TIME
static bool tic = false;
static bool running = true;
if (presses & 1) { // EK-TM4C1294XL button 1 pressed
@ -190,11 +190,66 @@ void ButtonISR(void) {
if (presses & 2) { // EK-TM4C1294XL button 2 pressed
gTime = 0;
}
if (presses & 4) { // boosterpack button 1 pressed
drawRequested = 1;
}
if (running) {
if (tic) gTime++; // increment time every other ISR call
tic = !tic;
}
#endif // DISPLAY_TIME
Button buttons[9];
uint8_t length, i;
length = buttons_from_mask(buttons, presses);
for (i=0; i<length; i++) {
fifo_put(buttons[i]);
}
}
uint8_t buttons_from_mask(Button *array, uint32_t buttons) {
uint8_t i, len;
len = 0;
for(i=0; i<9; i++) {
if (buttons & 1) {
array[len++] = (Button) i;
}
buttons >>= 1;
}
return len;
}
volatile DataType button_fifo[FIFO_SIZE]; // FIFO storage array
volatile int fifo_head = 0; // index of the first item in the FIFO
volatile int fifo_tail = 0; // index one step past the last item
// put data into the FIFO, skip if full
// returns 1 on success, 0 if FIFO was full
int fifo_put(DataType data)
{
int new_tail = fifo_tail + 1;
if (new_tail >= FIFO_SIZE) new_tail = 0; // wrap around
if (fifo_head != new_tail) { // if the FIFO is not full
button_fifo[fifo_tail] = data; // store data into the FIFO
fifo_tail = new_tail; // advance FIFO tail index
return 1; // success
}
return 0; // full
}
// get data from the FIFO
// returns 1 on success, 0 if FIFO was empty
int fifo_get(DataType *data)
{
if (fifo_head != fifo_tail) { // if the FIFO is not empty
*data = button_fifo[fifo_head]; // read data from the FIFO
if (fifo_head >= FIFO_SIZE-1) {
fifo_head = 0; // reset if past end
} else {
fifo_head++; // increment if within buffer
}
return 1; // success
}
return 0; // empty
}

View file

@ -27,8 +27,8 @@
#define BUTTON_AUTOREPEAT_INITIAL 100 // how many samples must read pressed before autorepeat starts
#define BUTTON_AUTOREPEAT_NEXT 10 // how many samples must read pressed before the next repetition
#define JOYSTICK_UPPER_PRESS_THRESHOLD 2100 // above this ADC value, button is pressed
#define JOYSTICK_UPPER_RELEASE_THRESHOLD 2000 // below this ADC value, button is released
#define JOYSTICK_UPPER_PRESS_THRESHOLD 2200 // above this ADC value, button is pressed
#define JOYSTICK_UPPER_RELEASE_THRESHOLD 2100 // below this ADC value, button is released
#define JOYSTICK_LOWER_PRESS_THRESHOLD 1700 // below this ADC value, button is pressed
#define JOYSTICK_LOWER_RELEASE_THRESHOLD 1800 // above this ADC value, button is released
@ -52,4 +52,26 @@ void ButtonReadJoystick(void);
// autorepeat button presses if a button is held long enough
uint32_t ButtonAutoRepeat(void);
typedef enum {
SW1 = 0,
SW2,
S1,
S2,
Select,
Right,
Left,
Up,
Down,
} Button;
// convert mask into array of buttons
//
// returns length
uint8_t buttons_from_mask(Button *array, uint32_t buttons);
#define FIFO_SIZE 11 // FIFO capacity is 1 item fewer
typedef Button DataType; // FIFO data type
int fifo_put(DataType data);
int fifo_get(DataType *data);
#endif /* BUTTONS_H_ */

153
main.c
View file

@ -13,6 +13,7 @@
#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"
@ -28,7 +29,43 @@
uint32_t gSystemClock; // [Hz] system clock frequency
volatile uint32_t gTime = 0; // time in hundredths of a second
volatile uint8_t drawRequested = 1;
volatile uint8_t refresh = 1;
// assumming 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,
};
uint32_t cputime_unloaded;
int Trigger(bool rising);
// start a pwm test signal
void start_signal() {
@ -52,6 +89,26 @@ void start_signal() {
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();
}
int main(void) {
IntMasterDisable();
@ -71,22 +128,61 @@ int main(void) {
GrContextInit(&sContext, &g_sCrystalfontz128x128); // Initialize the grlib graphics context
GrContextFontSet(&sContext, &g_sFontFixed6x8); // select font
#ifdef DISPLAY_TIME
uint32_t time; // local copy of gTime
#endif // DISPLAY_TIME
char str[50]; // string buffer
// full-screen rectangle
tRectangle rectFullScreen = {0, 0, GrContextDpyWidthGet(&sContext)-1, GrContextDpyHeightGet(&sContext)-1};
ButtonInit();
start_cputimer();
IntMasterEnable();
uint8_t voltage_scale = 0;
uint8_t time_scale = 0;
uint8_t rising = 1;
while (true) {
// 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.;
// handle buttons
Button button = (Button) 0;
while (fifo_get(&button)) {
switch (button) {
case S1: // toggle edge
rising = !rising;
break;
case S2: // draw
refresh = !refresh;
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;
}
}
GrContextForegroundSet(&sContext, ClrBlack);
GrRectFill(&sContext, &rectFullScreen); // fill screen with black
// assumming square lcd
#define HEIGHT LCD_VERTICAL_MAX
#define PIXELS_PER_DIV 20
GrContextForegroundSet(&sContext, ClrBlue);
// draw gridlines from the center out
@ -100,22 +196,39 @@ int main(void) {
}
// 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);
if (rising) {
GrStringDraw(&sContext, "^", /*length*/ -1, /*x*/ WIDTH - 10, /*y*/ 0, /*opaque*/ false);
} else {
GrStringDraw(&sContext, "v", /*length*/ -1, /*x*/ WIDTH - 10, /*y*/ 0, /*opaque*/ false);
}
// display graph
#define LOCAL_BUF_LEN 128
uint16_t local_adc_buffer[LOCAL_BUF_LEN]; // copy of adc buffer
int32_t adc_current_index;
int32_t j;
if (drawRequested) {
adc_current_index = gADCBufferIndex - 128;
if (refresh) {
int trigger = Trigger(rising);
adc_current_index = trigger - (WIDTH / 2);
for (j=0; j<LOCAL_BUF_LEN; j++) {
local_adc_buffer[j] = gADCBuffer[ADC_BUFFER_WRAP(adc_current_index + j)];
}
drawRequested = 0;
}
float fVoltsPerDiv = gVoltageScale[voltage_scale];
float fScale = (VIN_RANGE * PIXELS_PER_DIV)/((1 << ADC_BITS) * fVoltsPerDiv);
GrContextForegroundSet(&sContext, ClrPink);
for(j=0; j<LOCAL_BUF_LEN; j++) {
#define TRANSPOSE(x) (HEIGHT/2) - (x >> 6) // reduce from twelve to six bits, then flip and center
#define TRANSPOSE(x) (HEIGHT/2) - (int)roundf(fScale * ((int)x - ADC_OFFSET));
uint32_t upper,lower, current, last;
if (j==0) {
@ -171,3 +284,25 @@ int main(void) {
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 = x = gADCBufferIndex - (WIDTH / 2); // reset x back to how it was initialized
return x;
}

View file

@ -6,6 +6,7 @@
#include "driverlib/gpio.h"
#include "driverlib/sysctl.h"
#include "driverlib/interrupt.h"
#include "driverlib/timer.h"
#include "sysctl_pll.h"
#include "inc/hw_types.h"
#include "inc/hw_memmap.h"
@ -13,6 +14,8 @@
#include "buttons.h"
#include "sampling.h"
extern uint32_t gSystemClock; // [Hz] system clock frequency
// latest sample index
void ADC_ISR(void)
{
@ -41,6 +44,9 @@ void start_sampler() {
// initialize ADC peripherals
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC1);
// timer for low-speed
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER2);
// ADC clock
uint32_t pll_frequency = SysCtlFrequencyGet(CRYSTAL_FREQUENCY);
uint32_t pll_divisor =
@ -64,3 +70,25 @@ void start_sampler() {
// enable ADC1 sequence 0 interrupt in int. controller
IntEnable(INT_ADC1SS0);
}
void set_frequency(uint64_t microseconds) {
if (microseconds == 1) {
ADCSequenceDisable(ADC1_BASE, 0);
// don't use a timer for high speed
ADCSequenceConfigure(ADC1_BASE, 0, ADC_TRIGGER_ALWAYS, 0);
ADCSequenceEnable(ADC1_BASE, 0);
return;
}
// initialize timer 2 in periodic mode
TimerDisable(TIMER2_BASE, TIMER_BOTH);
TimerConfigure(TIMER2_BASE, TIMER_CFG_PERIODIC);
#define MICROSECONDS_PER_SECOND 1000000
TimerLoadSet(TIMER2_BASE, TIMER_A, (uint32_t) ((uint64_t) gSystemClock * microseconds / MICROSECONDS_PER_SECOND) - 1);
TimerControlTrigger(TIMER2_BASE, TIMER_A, true);
TimerEnable(TIMER2_BASE, TIMER_A);
ADCSequenceDisable(ADC1_BASE, 0);
ADCSequenceConfigure(ADC1_BASE, 0, ADC_TRIGGER_TIMER, 0);
ADCSequenceEnable(ADC1_BASE, 0);
}

View file

@ -15,4 +15,6 @@ volatile uint32_t gADCErrors; // number of missed ADC deadlines
// initialize ADC and ISR
void start_sampler(void);
void set_frequency(uint64_t microseconds);
#endif /* SAMPLING_H_ */