In embedded software, it's common to need to pass board-specific configuration data to a function.
In such cases, it's tempting to declare a set of const's (typically structures) to hold all the configuration values .
The downside of this approach is that memory is precious in embedded; defining lots of constants, not all of which may be used, is undesirable.
To give a concrete example
Suppose we're writing a library function to start-up a microprocessor's analog-to-digital converter:
fStartADC( pConfigData );
Here pConfigData is a pointer to a C-Structure containing configuration data for the ADC (for example the pin on the board you want the ADC to connect to).
If there are seven (say) possible ADC configurations, its tempting to declare a set of const. structures CONFIG0 ... CONFIG6, for users of your library function.
Should a user only make use of (say) CONFIG3 however, you risk having six unused structures bloating memory.
Instead, you'd like to provide users of your library a set of predefined structures CONFIG0 ... CONFIG6, but have the compiler only create (instantiate) those that the user actually uses.
C++ offers you such things as constructors to help with this. In C it takes a little more work, but here's one way:
To keep things simple, suppose our ADC CONFIG structure needs to hold a single integer, x (in real life, configuring the ADC need require multiple e.g. pin, port, oscillator... etc. values)
First, we declare a type for our structure and a pointer to it.
typedef struct { int x; } ConfigData_t, *pConfigData_t
Next, we define a function that takes an integer, creates a structure containing the integer, and returns a pointer to it.
pConfigStruc_t fConstructor( int x ) {
static ConfigStruc_t CONFIGn;
CONFIGn.x = x;
return &CONFIGn; }
Finally, we define some macro's
#define CONFIG0 ( fConstructor ( 7) )
#define CONFIG1 ( fConstructor (67) )
..etc.
where 7, 67 are examples of board-specific configuration values.
We can now offer users of our library, a set of predefined configurations CONFIG0,CONFIG1 insulating (abstracting) them from any messy details of our board hardware. The user is free to write a line of code like:
fStartADC( CONFIG0 );
At compile-time, in accordance with our #define, the compiler will dutifully replace CONFIG0 with fConstructor(7)
fConstructor (7) will then do its job, returning a pointer to a structure containing '7'.
Notice however, that since our user never wrote CONFIG1, a structure holding 67 was never created!
We've met our goal of providing users with a set of predefined CONIG's but at the same time made sure that only those they use will be compiled into memory. Neat huh!
To see a 'real world' example of this technique, check-out my gitHub code here.
Showing posts with label embedded software. Show all posts
Showing posts with label embedded software. Show all posts
Saturday, 29 October 2016
Wednesday, 12 October 2016
Embedded Basics No.1 - Registers
A core
concept in embedded-electronics is the register.
For embedded newbies, put simply,
a register is a ‘box’ that stores a number
‘Stores’
means a register is a memory device. Place (embedded programmers say
‘poke’) a number into the ‘box’ (register), and it’s held
there until you decide to change it or you power-off the IC.
Other parts of an electronic system may be able to read a number
you’ve poked into a register, and adjust their behavior based on its value. Figure 1 shows the general idea. Here a 3-bit register controls 3 LED's. Poking the value '101' turns two LED's on, and poking '010', one LED.
Modern IC’s may
contain hundreds of registers, each controlling a different feature
of the device. Manufacturers therefore need to give you a way to single-out an individual register. They do this by giving each
register a unique name or number termed its “address”. You use
the address to send the IC instructions along the lines:
“Hi Mr. IC, poke the value 17 to the register with address 82”
“Hi Mr. IC, poke 23 to the register TXCTRL”
“Hi Mr. IC, what's the value in register RXBUFF?”
Here “TXCTRL” and
“RXBUIFF” are example register names (its traditional to use
uppercase lettering for registers).
The first two instructions are write-instructions. The third is a
read-instruction. Not all registers allow both reads and writes. For
example, a manufacturer might include a read-only register in an IC
to store a serial number, giving you the ability to read the number, but
not over-write it.
Some registers contain both write’able and read-only regions. For
example, a register might store an 8-digit (bit) number, but only
allow you to write (change) seven of these bits, the eighth bit being
permanently fixed (at ‘1’ say).
It can also be
important to know what value may be present in a register before you write anything to it (the default value present in the register immediately after you’ve
powered-up the chip for example).
All the various properties of an chip's registers are stated in the datasheet in a (fairly) standard format. Figure 2 shows a typical (fictitious) example.
In words, the table can be understood as saying:
- CFG0 is the name of a register name with address "127" (= 7F in hex)
- The register stores an 8-bit (binary) value. The individual bits are themselves named (LED0, LED1...CTRL).
- Bits 0 - 4 can be both written (‘W’) and read (‘R’).
- Bit 5 - 6 can be read, but not written (changed).
- Bit 7 can be written but not read.
- Bits 1,2,3,5,6 have default value '0'
- Bits 0,4 have default value '1'
- Bit 7 has an undefined ('U') default value
In addition to the table, the datasheet will describe what each section of the register does. For example the datasheet might describe the function of the 'LED0' bit by saying
- LED0: Writing '1'('0') turns the on-board LED#1 on (off)
(By the way, precisely how you send register-read/write instructions to a chip depends on the device. For some, you might send instructions via an (e.g. SPI or I2C) interface; A microprocessor might have the instruction written into its software. Written in C (you can skip this bit if you don’t know C, but you should be aware that a large proportion of embedded software uses it), the second example instruction above might look something like
*(TXCTRL) = 23
I should add there's a lot more could be said here about pointers, volatiles etc. but these are software topics for another time).
Finally, at the
hardware level, a register is typically just a row of (one or more)
edge-triggered D-type flip flops. Unless you’re heavily into
transistor-level integrated-circuit design, it’s rarely necessary
to understand the inner workings of flip flops, so I won’t get into details here. Wikipedia is the place to start if you’re hungry
for more. It’s enough to know that a ‘1’ (or a ‘0’)
put on the ‘D’ input of a flip-flop, will be captured and stored
if a rising-edge (a voltage step) is applied the ‘Clk’ input.
The stored ‘1’ (or ‘0’) will show up on the flip-flop's ‘Q’
output. A row of D-type flip flops allows you store a set of '0’s and
‘1’s – in other words it forms a register.
The ability to arrange
and manipulate registers is so important in digital system design
(for example, when creating ASIC's or programming
FPGA’s) that dedicated hardware design languages (HDL’s) such as
VHDL and Verilog are available to help streamline the task. In a language like VHDL, you routinely see lines of code like
If
rising_edge(clock) then A <= “10101010”;
This translates as an instruction to your FPGA / ASIC design- tools that you want a (clocked)
register with an output called “A”, storing the
value “10101010”.
I’ll end my brief
introduction to registers here.
Do drop me a comment on
this, or any of the others topics on this blog.
Sunday, 9 October 2016
Introducing the Tiva Lauchpad
Everyone knows the Arduino (/Genuino), but some may less familiar with the Texas Instruments Tiva LaunchPad.
I’m planning to use this in some future posts, so today is a quick intro. to this nifty little board. All views here are my own by the way - I'm not paid to give them!
I’m planning to use this in some future posts, so today is a quick intro. to this nifty little board. All views here are my own by the way - I'm not paid to give them!
The Launchpad is one of a bunch of low-cost boards built around ARM microcontrollers. Others include the Nucleo and boards by RedBear.
Working with these boards is similar to working with the Genuino. You write code on your PC and download it to the board via USB.
What the Launchpad (full name Tiva-C TM4C123 Launchpad) gives you compared to the Arduino is much-increased processing power and added features.
On the hardware side, for less than $20, you get an 80MHz ARM M4 processor; 256K of flash memory; a 12-bit ADC; multiple (USB, I2C, SPI, UART and CAN) interfaces; 43 GPIO’s; and several others features including PWM, Comparator and DMA modules. The board doesn't have wireless or ethernet, but there're still plenty of projects you can do without these. (Other boards in the Tiva series do have wireless connectivity - I'll take about these another time).
On the software side the Launchpad is a great way to grow your embedded skills. At first, you might choose to program it using the quick-and-easy Energia software environment. This makes writing Tiva software look-and-feel like Arduino code. Or you might download Texas Instruments’ (free) Code Composer Studio environment. This comes with ready-made C-code examples (an API) that help you access the Lauchpad's features. Ultimately you can move on to advanced embedded software techniques such as RTOS programming and the ARM CMSIS.
All round, the Launchpad is great value and has enough power to deliver serious results. It's something I'll be using frequently in future posts.
Subscribe to:
Posts (Atom)

