bFunc - Project Journal - Week Seven
Here’s the build log for week seven of development for the open source function generator I’m trying to prototype and build.
Note: The prototypes are just about ready for human consumption!! Do you want to try one out? Head over to this page to get the download.
Each day’s entry represents an hour’s work per day - the hour before I leave for my job every weekday morning.
This is my log for the week ending March 21, 2020. Here’s links to the prior weeks’ logs:
The design doc for this project is available if you’re into design documents/philosophy.
All of my hardware and software source files are on GitHub. Check ‘em out!
March 20, 2020
What’s the full set of commands I can pass this thing via serial port?
sine
- sets a sine wave as outputsquare
- sets a square wave as outputtriangle
- sets a triangle wave as outputidle
- idles the outputfreq
- sets the frequency of the current waveformphase
- sets the phase of the current waveform
Graynomad’s post on AVRFreaks is instructive on how to stack this up into command table
- Make a structure for each command:
typedef struct command { char cmd[10]; void (*func) (void); }
- Make an array of command
struct
objects to compare against - Loop through that array when the
return
key is pressed strcmp
through the.cmd
member of each object in the command table- Call the associated function pointer when
.cmd
and the input string match
How does the behavior change for the different commands called?
- It’s not quite as straightforward as the AVRFreaks example
- The behavior of the function changes based on the contents of the first parameter
sine
/square
/triangle
each expect a freq and phase argidle
expects no argsfreq
/phase
expect one numeric arg
This makes me think that the better way to implement Graynomand’s underlying command struct is something like this:
struct command { char cmd[10]; enum states state; }
- That way your command parsing table is just populating an instance of
struct ad9837_ctrl_reg
- The command completes by transmitting those bytes via
HAL_SPI_Transmit()
- That way your command parsing table is just populating an instance of
March 19 2020
Lost my working time this morning due to some insomnia, and sleeping a little late to compensate
I did manage to find some time to plug the board into a Windows laptop, and everything worked fine!
Backspacing, incredibly, works as expected!
On my Mac, it does not set the cursor back. Need to figure out why. Guessing it’s from the serial consoles sending different characters for the “backspace” key
…but my Mac doesn’t HAVE a backspace key - it’s a “DELETE” key!
This GitHub Issue also has a useful explanation
March 18 2020
- First step - determine waveform setting/action based upon first tokenized subfield
- Positional parsing/setting
- Ex:
sine 10000 45
produces a 10kHz sine wave with a 45 degree offset - Suppose we split that into three space-delimited substrings
- Positional parsing means that the command line knows:
- field[0] == WAVEFORM
- field[1] == FREQUENCY
- field[2] == PHASE
- One way of doing this is just keeping an integer variable that keeps track of the positional array via pointers
- This works, but is starting to overload
ProcessCommand()
a bit. - Should probably break out into subfunctions:
ParseWaveform()
- takes first subfield as argument, returnsenum states
for new state (or NULL/-1 for no change/input error)ParseFrequency()
- takes second subfield as argument, returns integer frequency (or NULL/-1 for no change/input error)ParsePhase()
- takes third subfield as argument, returns integer phase (or NULL/-1 for no change/input error)- Should likely expand to phase as a
float
but an integer is good enough for now
- Should likely expand to phase as a
- This works, but is starting to overload
- Checking the size of my binaries - Using all these
string.h
function calls has me idly concerned that I’ll run out of flash.- Some great explanation on interpreting
arm-none-eabi-size
results from the folks over at Memfault Consider swiping Francois’s size shell script - that’s a clever trick!
- The Memfault post also included a link to a Google tool for easy binary size analysis too: Bloaty McBloatFace
- Some great explanation on interpreting
March 17 2020
- Working to include
<string.h>
and a parser based onstrtok()
- Easier than I thought it’d be -
strtok()
replaces the delimiter character with a null character, so you can pass the returned pointer to aprintf()
function. - In my case, I can save a pointer for the start of a delimited string, and use the pointer returned by
strtok()
to figure out the length. length = token_pointer - start_pointer;
- Easier than I thought it’d be -
- Parsing in
ProcessCommand()
is working now!- Can delimit the inputs of
UserRxBufferFS
by space separators - Next step is to compare the subfields of
ProcessCommand()
to a command table - Next step after that is to execute function calls based on the delimited strings
- Can delimit the inputs of
- Cool! Particularly productive hour of work. :D
Mar 16 2020
- Starting again on command parsing
- We can load up a buffer in memory using the USB CDC interface - it’s time to split it into substrings and make decisions based on those substrings
strtok()
is a useful function for this, but requires the<string.h>
library, and I’m not 100% sure we can use that here- Wrote a simple parser function that prints space-delimited substrings
- Note: when you hit “enter” on a screen session to process command, it’s
case ‘\r’
that the switch statement breaks on
- String manipulation and parsing is proving to be the most challenging part of this project thus far XD
- I’m not bad at hacking through register settings and peripheral configurations, but old school string parsing is a subset of computer science curriculum I think I may have skipped over entirely
- Man, fuck it - do I wanna write a whole parser? Not really!
- Let’s figure out how to add
<string.h>
to this beez. - Well - it doesn’t choke when we add the
<string.h>
include statement! That’s a start!
- Let’s figure out how to add