Basics of Structures in C

With this post, we get to one of the most useful elements of C: Structures. While structures didn’t exist in some of the early versions of C, they were soon added in as the developers of C and the Unix operating system saw how useful they could be for extending the utility of their pre-existing programs, and indeed, developing programs outside of the field of system programming. The C data structure is, along with functions, one of the elements that gives it extra utility compared to the programming languages that came before, like FORTRAN and BASIC.

A structure is, speaking succinctly, a way of wrapping multiple variables up into one “package”. To demonstrate why you might want to do this, we could say that you’re designing a simple data processing application like a rudimentary stock control system for a small shop. This stock control system works on a number of different variables, such as char arrays for storing the names of the products, float variables for storing the cost and sell price of these products, and int variables for storing the number of products left in stock. Without structures, we have to declare all of these variables discretely, as illustrated below:

char *name[100];

int *number;

double *cost;

double *price;

The reason we have declared all of these as pointers is because we will want to declare arrays of these variables, but unless you know exactly how many product types are in your database, you won’t know how much memory to allocate, and so, you will have to use malloc(), calloc() or realloc() to create arrays of the appropriate size.

The problem we have here is that none of the elements of these arrays has any particular connection to the corresponding elements in the other arrays. Let’s say you wanted to sort the products into alphabetical order. You’d perform your sorting algorithm on the name[] array, which would work perfectly well, but if you forgot to appropriately change around the other three arrays, you’d end up with a situation where all of the arrays were knocked out of sync. This demonstrates how fragile this stock control system would be – and how inefficient, as you’d have to appropriately change the positions of three other arrays in addition to the name[] array!

What’s more, this particular stock control program wouldn’t work very well when it came to passing the elements into a function. In order to perform a stock reordering function, what we’d have to do is pass four separate variables into the function. Let’s say that we just wanted to reorder a single product, which might use a function with a prototype like this:

int reorder(char **name, int *number, double *cost, double *price);

Pretty messy, don’t you think? One of the preferable things about structures is that they allow you to send a single variable into this function. The function prototype can be rewritten in the following fashion:

int reorder(struct item *product);

This is a lot cleaner and easier to handle. There’s a single variable in the arguments of type “pointer to struct item“, which contains all of the discrete variables which we had before. I’ll explain more about pointers to structures later. First of all, we’re going to leave our stock control system for a few moments and look at a simpler structure declaration.

One useful application that C can and regularly has been used for is graphical manipulation. In a raster graphical format, such as BMP, JPEG, GIF or PNG, each pixel has an x and a y coordinate. In the light of what we’ve seen above with the lack of robustness with discrete variables, we’re going to want to create a “package” – or structure – to hold both components of a pixel location together. We’ll create a structure type for doing this:

struct pixel {
    int x;
    int y;
};

What we’ve just created is a structure declaration, which essentially says, “Let’s create a new type, called struct pixel, whose elements will be an int variable named x, and an int variable named y“. This structure declaration now allows us to create variables of type struct pixel in other parts of the program.

Once we have the structure declaration, we create an element of this structure type like this:

struct pixel pixel_1;

The element pixel_1 is now created, just as we’d create an int, char or float variable. It follows some of the same rules as any of these other variables, in that it fills a certain amount of space (in this case, as it contains two int variables, its size will be sizeof(2 * int)). However, because it contains more than one variable itself, changing the values of these internal variables involves a different step than we’ve taken before.

To do this, we introduce the structure member operator, “.” (a single full stop). Let’s illustrate how this works by setting the value of the x coordinate in pixel_1 to 150:

pixel_1.x = 150;

The structure member operator is very high in the order of precedence in C; it has a higher precedence than the pointer dereference operator, and equivalent precedence to brackets and square brackets.

We’ll put this all together in a simple program:

#include <stdio.h>

struct pixel {
    int x;
    int y;
};

int main(void)
{
    /* We'll create a variable of type struct pixel */
    struct pixel pixel_1;

    /* Set the elements of pixel_1 /*
    pixel1.x = 200;
    pixel1.y = 350;
    return 0;
}

The elements of a structure can be any legal variable, such as int, char, float and double. Notably, a structure, once it has been declared, is a legal variable. This means that structures are themselves legal elements of a structure – as long as they are structures of a different type. Extending our metaphor of a graphics program, we can use these so-called nested structures to create a representation of a rectangle. Our simple representation of a rectangle will consist of two pixels representing the origin and the diagonally opposite corner:

struct rect {
    struct pixel pt_1;
    struct pixel pt_2;
};

Indeed, the dimensions of a standard computer screen can be represented by a rectangular structure. We will illustrate how we would use our nested structure here for a 1920×1200 screen:

struct rect screen;

screen.pt_1.x = 0;
screen.pt_1.y = 0;
screen.pt_2.x = 1919;
screen.pt_2.y = 1199;

Unfortunately, without graphical functions to act upon these structures, we can’t do much with our pixel and rectangle structures. We return, therefore, to the stock control program described earlier. Having seen some simple structures, we can now design a structure which will represent a data entry.

struct item {
  char name[100];
  int number;
  float cost;
  float price;
};

This structure contains the variables for which we would have had to declare separate arrays in the previous program. In using a structure, we have packaged all of these variables together, giving them a mutual association. We’ll demonstrate how this may be used, given a database containing the following, which we can save to a file:

Bread 12 1.65 2.25
Milk 23 1.40 1.80
Eggs 20 1.60 2.15
Cheese 11 1.85 2.50
Bacon 6 2.65 3.15

Now, we write a program to take these values in and to print them out. This program will take a single additional command-line argument, which will be the file name of the database that we defined above:

#include <stdio.h>

struct item {
    char name[100];
    int number;
    float cost;
    float price;
};

int main(int argc, char *argv[])
{
    struct item item_array[5];
    FILE *ifp;
    int i;

    if (argc != 2) {
	printf("Usage: basic_database <filename>\n");
	return -1;
    } else if ((ifp = fopen(argv[1], "r")) == NULL) {
	printf("Error: Could not open file %s\n", argv[1]);
	return -2;
    } else {
	for (i = 0; i <= 5; i++) {
	    fscanf(ifp, "%s%d%f%f", item_array[i].name,
	    &item_array[i].number,
	    &item_array[i].price, &item_array[i].cost);
	}
	fclose(ifp);
    }

    for (i = 0; i <= 4; i++) {
	printf("%s %5d %8.2f %8.2f\n", item_array[i].name, item_array[i].number,
	item_array[i].price, item_array[i].cost);
    }
    return 0;
}

When we call it with our data from before, we get the following result:

Bread 12 1.65 2.25 
Milk 23 1.40 1.80 
Eggs 20 1.60 2.15 
Cheese 11 1.85 2.50 
Bacon 6 2.65 3.15

So, we’ve defined the framework for a simple database. We could do things with these values, such as searching through them to find the item with the highest price for the consumer:

float highest_price = 0;
int highest_price_pos = 0;

for (i = 0; i < 5; i++) {
    if (item_array[i].price > highest_price) {
	highest_price = item_array[i].price;
	highest_price_pos = i;
    }
}

A problem arises, though, when we look at this in greater detail. What happens if the database – as is very likely – contains more than five items? We don’t want to have to recompile our program every time we get a new item to sell, so we do what we would do if we required additional variables of other types: We use dynamic memory allocation. In order to do this, we will require a pointer to our structure type, but before I change our program, we need to discuss pointers to structures.

Pointers to structures operate similarly to pointers to other variables, in that they store memory addresses to structures. They are also declared similarly, with the following example being a pointer to type struct item:

struct item *item_pointer;

However, the order of precedence in C creates a bit of a problem when it comes to using the traditional dereference operator to access the contents of a pointer to a structure. The traditional syntax for accessing data in the elements of a pointer to a structure looks peculiar and confusing:

item_pointer = item_array[3];

(*item_pointer).price = 2.50;

Note that we have to use brackets around the dereference operator and the thing it’s dereferencing. The order of precedence for the structure member operator is higher than that of the dereference operator, and therefore we have to use brackets, which have the same precedence as the structure member operator, in order to allow the pointer to access its memory address before the structure member is called.

Luckily, there is a separate operator used specifically for allowing a pointer to a structure to access its elements without having the potential source of confusion caused by the dereference operator in this circumstance. The structure pointer operator, -> (a minus sign, followed by a greater-than sign), has the same precedence as the structure member operator, and reduces the ambiguity existing with the traditional syntax.

Having discussed that, we can modify our program from before to dynamically allocate memory based on the number of elements contained in the list. We will use malloc() to initially allocate memory for a single element of type struct item *, then use fscanf() to read in the values into the elements of this structure. For each line that fscanf() doesn’t read in the EOF character, the program increases the size of the dynamically allocated memory by the size of the struct item type using the realloc() function. Then, the elements of the structure array are read out as above.

#include <stdio.h>
#include <stdlib.h>

struct item {
    char name[100];
    int number;
    float cost;
    float price;
};

int main(int argc, char *argv[])
{
    struct item *item_array;
    FILE *ifp;
    int i;
    int array_elements = 0;
    int el_size;

    /* Store the size of a single element */
    el_size = sizeof(struct item);

    /* Start off by declaring a single element */
    item_array = (struct item *) malloc (el_size);

    if (argc != 2) {
	printf("Usage: basic_database <filename>\n");
	return -1;
    } else if ((ifp = fopen(argv[1], "r")) == NULL) {
	printf("Error: Could not open file %s\n", argv[1]);
	return -2;
    } else {
	/* Keep going until fscanf hits the EOF character */
	while (fscanf(ifp, "%s%d%f%f", item_array[array_elements].name,
		      &item_array[array_elements].number,
		      &item_array[array_elements].cost,
		      &item_array[array_elements].price) != EOF) {
	    /* Reallocate memory, then increment the elements counter */
	    /* NOTE: We've got a bodge in here. The memory allocated will
	     * always be one element more than we need. */
	    item_array = (struct item *)
		realloc(item_array, (el_size * ++array_elements) + el_size);
	    if (item_array == NULL) {
		printf("Error: Cannot allocate memory\n");
		return -1;
	    }
	}
	fclose(ifp);
    }
    for (i = 0; i < array_elements; i++) {
	printf("%s %d %.2f %.2f\n", item_array[i].name, item_array[i].number,
	       item_array[i].cost, item_array[i].price);
    }

    free(item_array);
    return 0;
}

The Unexpected Satellite – Part 4

After several hours of discomfort for all of the crew members, the Chronos had finally reached a velocity more suitable for making contact with the stranded asteroid miner. Alan had switched the centrifuge in the main crew compartment back on, but it would take several minutes for the motors to defeat the inertia of the heavy disc that comprised that part of the spacecraft. In the meantime, the Commander and Vice-Commander traded reports about various navigational and scanning issues. The evidence was becoming clearer: There certainly was a metallic object of substantial proportions proceeding on the trajectory that the ECSA had predicted for the asteroid miner, large enough to be a spacecraft and, to the relief of all, appearing to be in one piece.

As Alan rolled up his reading screen and placed it into his pocket, Gerhard, the mathematician on board the Chronos, pulled himself out of his seat and floated over to Alan.

Is the crew compartment almost prepared?” Gerhard asked, in a German accent which Alan perceived with his paltry knowledge of German to have been tempered somewhat by years of speaking English.

Yeah, Gerhard,” Alan replied. “Just waiting for the centrifuge to spin up fully. Do you have those read-outs handy?”

Yes, yes,” Gerhard said with an air of reassurance. “They’re on my tablet.”

Right, then, Gerhard. I’ll see you in the recreational room, then?”

Gerhard raised his hand in a salute, turning towards the hatch in the centre of the room and pushing himself towards the ceiling to grip a handhold. Alan checked his wrist computer to check the rotational speed of the centrifuge, seeing that it was close to full speed, and unstrapped himself from his chair. He shouted over to Gerhard, telling him that he could pass through the hatch, then pushed himself up to the ceiling, pulling himself over to Andrew’s chair.

Andrew, by this point, was slumped over his chair in a deep slumber. Alan knew all too well the exhaustion that was inflicted on inexperienced spacecraft crew members, but there was work to be done. Gingerly, Alan pushed himself down and gently prodded Andrew in the shoulder with the toe of his boot. After a few attempts, Alan finally elicited a response as Andrew groggily shook his head and murmured some barely understandable words, “Whadda you want?”

Time to wake up,” Alan replied, “We’ve got work to do.”

After a few seconds’ pause as Andrew awkwardly fumbled with the straps of his chair, he managed to free himself and push himself out of his chair. Alan, not content to wait for Andrew to wake up properly, pulled himself towards the central hatch and plunged into the tunnel beneath him.

A couple of minutes later, he made his way back into the crew compartment and proceeded towards the recreational room. Once he arrived there, he saw Gerhard sitting at one of the tables, looking inquisitively at his tablet. As Alan opened the door, Gerhard raised his head, waved his hand and took another brief glance at his tablet while Alan found a seat on the opposite side of the table.

Where’s the computer expert?” Gerhard asked as Alan adjusted his chair.

Andrew? He’ll be down in a few moments. I just woke him up a few minutes back.”

While Alan and Gerhard waited for Andrew to arrive, they briefly discussed some of the technical details that they had arranged the meeting about. “I’d like to get as close to the asteroid miner as possible on autopilot,” Alan said. “That entry hatch looks awkward to get into, even if you’re a confident shuttle pilot.”

Well,” Gerhard replied, “we can go for a preliminary approximation here and try refining our guess when we have more information on the situation, right?”

Yeah, that sounds like a good idea. We’ll have to agree on the loadout on the shuttle here and now, though. I’ll take on some extra fuel just in case, but adding extra inertia doesn’t sound like my idea of a good time.”

Suddenly, the door slid open. Andrew stepped into the room, with a rather dishevelled look and bags around his eyes. “Sorry for keeping you,” he apologised as he sat down beside Alan.

Don’t worry about it,” replied Alan and Gerhard in unison. “Now that we’re all here, we can begin,” Alan continued once Andrew had made himself comfortable.

After some preliminary discussion of the mission profile, with some of the details reiterated for the benefit of Gerhard, Gerhard began to speak.

So, we’re basing this on a one-hundred kilometre round journey with some additional fuel for manoeuvring around the asteroid miner, correct?”

That’s correct,” Alan replied. “We’ll also have to take into consideration the mass of our payload, including myself and Andrew, my toolkit, the computer nodes and whatever other equipment and ephemera we need. Andrew, do you know off-hand how heavy each of those computer nodes are?”

I think about 10 to 12 kilograms,” Andrew replied. “The specification sheet says 11.2 kilograms, as far as I remember, but that’s for a certain standard which the ones we have on board don’t conform to.”

Well,” Alan asked, “that’ll do well enough for an approximation, won’t it, Gerhard?”

Replying in the affirmative, Gerhard proceeded to enter some preliminary data into his tablet, drawing up the framework of an equation in the process. Stopping occasionally to ask questions, Gerhard continued his work in relative silence while Alan and Andrew discussed and occasionally argued about the equipment that they would need.

Are you sure that we need to bring all thirty-six nodes at once?” Alan asked as Gerhard continued on with his calculations. “I mean, we don’t even know if the power is on in the asteroid miner, and we don’t have anywhere to put them on the other spacecraft if we need to come back to get the equipment to work on the generator over there.”

We don’t need to bring all thirty-six,” Andrew conceded, “but I will say that I’d prefer if we brought the lot in one go. It’ll save us time later in terms of going back and forth.”

Right, well, you’re bringing that equipment down to the shuttle bay then. I’m going to need to get the plasma cutter out of storage anyway.” Alan paused for a second, then continued with a sardonic undertone, “I’m pretty convinced that the power in the asteroid miner’s gone, and that we’ll have to cut our way through the doors.”

Andrew ignored him, and the room remained in silence for the next few minutes while Gerhard continued to calculate. Suddenly, Gerhard raised his head and said, “Alright, gentlemen, I have all the information I need so far. Alan, I’ll discuss this with you later. Andrew, thank you for the assistance. Good day!”

* * *

The following day saw Alan and the rest of the crew of the Chronos almost swept off their feet with work. The spacecraft was close to its objective, and the engines had been set to act at a slow but consistent rate to slow the spacecraft to a constant velocity in respect to the stranded asteroid miner. In the meantime, the crew of the Chronos were making sure that everything was in order, with Alan’s subordinates in the maintenance department taking over Alan’s duties while Alan helped to prepare the shuttle for launch.

Andrew, for his part, was providing ample assistance, quickly proceeding to place the computer components neatly into the cargo bay of the shuttle, and once Alan had managed to wrestle the plasma cutter and its large gas cylinder from the storage bay, Andrew gracefully agreed to carry down the cutting equipment while Alan lugged the gas cylinder through the narrow corridors and tunnels leading to the shuttle bay.

After a few moments of wrestling the cumbersome cylinder around door edges and corners, Alan lifted the gas cylinder up the rear-mounted ramp of the boxy, bluff-nosed shuttle and dropped it with a clatter in a corner of the rear compartment. Alan could see Commander Jackson and Vice-Commander Matthews controlling the refuelling cycle of the shuttle from a remote terminal on a raised platform, while Gerhard could be seen through the door leading to the cockpit, programming the flight plan into the shuttle’s computers.

Resting for a moment against one of the side walls of the shuttle, Alan briefly considered the upcoming shuttle flight. The one-hundred kilometre distance between the Chronos and the asteroid miner wasn’t far off touching distance in interplanetary distances, but was a bit further in terms of the shuttle he would be flying. Most of the distance would be covered under the control of Gerhard’s flight program, with Alan and Andrew just passengers until the end. It was the final five hundred metres to a kilometre that Alan had to fly, and the most potentially fraught with danger.

Almost as soon as Alan had returned to work, carefully strapping the gas cylinder to the side of the spacecraft with an aluminium chain passed through a set of wall-mounted loops, the doors of the shuttle bay opened. Alan looked over his shoulder and saw Andrew walking in, arms filled with the box with the plasma cutter equipment.

Hey Alan, where should I leave this?”, Andrew shouted as he continued towards the shuttle ramp.

On the ground here, beside the wall,” Alan replied loudly. “Don’t worry about being exact – I’ll secure it to the shuttle while you get the computer components.”

Once Andrew had ascended the ramp and placed the equipment box onto the ground, Alan started dragging it over to the wall, where he began feeding another chain through the handles and wrapping it around a few loops on the wall. As Andrew was walking down the ramp, Alan suddenly called out, “Hey, Andrew, have you got a container for those computers, something I can secure to the wall?”

Uhh… no,” Andrew replied. “I still don’t get why you need to fix everything to the wall anyway.”

Keeps everything from being thrown about during acceleration, of course,” Alan replied. “There should be some appropriate boxes in the storage area – go for the lighter plastic ones. Don’t want to add more weight to the equation.”

Historical Operating Systems Reborn – RISC OS and the Raspberry Pi

The early-1980s 8-bit microcomputer battle brought the personal computer from a hobbyist’s plaything to a genuinely useful device for general use, and was fought by a host of companies. Most of these companies were from the United States, such as Commodore, IBM, Apple and Atari, but various British companies played a significant part including Sinclair, Amstrad and Acorn. By the mid-1980s, many of the smaller competitors had fallen by the wayside, and even the once-strong Sinclair Research had been bought up by Amstrad.

The big players who remained decided to produce more powerful machines using newer processors than the MOS 6502 and Zilog Z80 8-bit processors common in the early 1980s. Commodore bought up the Amiga Corporation, which had designed an eponymous computer; Apple designed the Macintosh; Atari developed the Atari ST and IBM continued to develop on their IBM PC platform. Most of these computer designs, with the notable exception of the IBM PC, were based around the Motorola 68000 processor. As Amstrad decided to focus on their PCW series of word processors, discontinuing the disappointing Sinclair QL, this left Acorn alone in the British market to try to fight out the battle of the post-8-bit era.

Acorn decided to take a different approach to the American companies, focusing on the educational segment rather than the business, desktop publishing and multimedia markets focused on by Commodore, Apple, Atari and IBM. Instead of using the Motorola 68000 processor familiar to other computers of the time, Acorn decided to design their own processor, using the then-novel RISC architectural design to develop the Acorn RISC Machine processor, better known as ARM.

In 1987, Acorn released the Archimedes. The ARM2 processor which Acorn used proved to be a great advantage for the Archimedes, with a simple, power-efficient design which nevertheless performed calculations about twice as quickly as a 68000 processor with the same clock speed. Allied to the ARM processor was Acorn’s Arthur operating system, which came on a ROM chip similar to the Amiga’s Kickstart ROM. Arthur, on balance, was on par or not far behind the Commodore Amiga’s notoriously advanced OS, and ahead of the single-tasking operating systems used by Atari and IBM.

AcornArchimedes-Wiki

The Acorn Archimedes – one of the several advanced computers of the late 1980s.

Unfortunately for Acorn, the Archimedes was not a particular sales success. Its focus on the educational market had come at the cost of the multimedia coprocessors available in the Amiga and Atari ST, leading to a system that was too expensive and not good enough at gaming for a home audience. Meanwhile, the business market became consumed by IBM and the various clones which arose from the easily-reverse-engineered BIOS of the IBM PC and its successors. Nevertheless, Acorn persisted and continued to develop new machines with more advanced operating systems. Arthur was updated, becoming RISC OS in the process, keeping to the same general structure but gaining new features.

Eventually, Acorn fell to the wayside, suffering a similar ignominious fate to Commodore and Atari as the personal computer market gradually became dominated by IBM-compatible computers with Intel processors. Apple managed to cling onto life during some very slim years, moving to the PowerPC architecture along the way, but eventually gave in and took up the Intel x86 processors as well, moving their BSD-derived Mac OS X operating system over to the new architecture.

Acorn has had one significant lasting legacy, however – the intellectual properties for the ARM processor were divested in a new company, ARM Holdings, who collaborated with Apple to continue developing the ARM architecture for Apple’s own devices. Today, the ARM processor is the most popular 32-bit processor architecture in the world, underpinning everything from smartphones and tablets to embedded processors inside other devices.

RISC OS has survived as well, with the intellectual property for the Acorn computers sold to Castle Technology Ltd., a small British company who has continued to develop ARM-based personal computers using RISC OS. A small but dedicated community grew up around the company, much like the remnants of the Amiga or Atari ST communities, and has continued to support the OS.

Now, we have the Raspberry Pi. The inexpensive, credit-card-sized computer has been a massive success, demonstrating a far more simple, hackable approach to computing than has been usual today. Something that has been a pleasant surprise is how readily the RISC OS community has decided to support the Raspberry Pi.

Given that until recently, I haven’t had a computer without an Intel processor, I didn’t have an opportunity to try RISC OS on anything but an emulator. However, I sometimes despair for the sheer homogeneity of the personal computer market, even though I have contributed to it for many years. Now, I have been granted a chance to try an operating system natively on modern hardware that isn’t part of the Microsoft Windows, Mac OS X or Linux families.

My initial thoughts when I first booted up RISC OS 5 were that it actually boots up as astoundingly quickly as others said it would. Frankly, this shouldn’t have been a surprise; not only is RISC OS still designed with the StrongARM processors of the Acorn Risc PC in mind, it is still developed for a 6MB ROM chip, and is therefore extraordinarily tuned for its environment. I had used RISC OS before on the ArcEm emulator about four years ago, so I recognised that RISC OS was slim and fast in the early 1990s, but it’s nice to see that this behaviour persists today. The same sort of responsiveness applies to the shutdown process as well. RISC OS has instant shutdowns. None of this behaviour where shutdowns can take almost as long as the boot process – as soon as you click the Shutdown option, short of certain file operations being in progress, the computer will immediately be ready to shut down.

After about ten to fifteen seconds, the GUI environment booted up. Two things were quickly apparent. The first is that the environment was immediately responsive as soon as it had finished loading, unlike contemporary Windows or Linux desktop environments, which, based on the number of background processes that are set to start – can leave you waiting a minute or more for full responsiveness.

The second thing is that the RISC OS GUI environment is, in fact, very pretty. Mac OS X and iOS are often held up as being the exemplars of pretty environments, but I’d argue that RISC OS is, in its own ways, marginally prettier. Much of what Mac OS X does to ensure its pretty environment is down to impressive, shiny graphics and high-resolution displays, whereas RISC OS manages to look good at 640×480 on a simple non-high-definition television screen.

A lot of this is down to the inherent design philosophy of RISC OS. The original Arthur OS for the Archimedes was the first operating system to incorporate a dock, or in RISC OS parlance, an icon bar. The icon bar distinguishes between application icons, set to the right-hand side of the icon bar, and storage devices, set to the left-hand side of the bar. This helps to create a distinct divide between applications and devices which store applications and data. In comparison, the Mac OS X dock can occasionally look a bit untidy and busy when you load up too many applications at once.

Another detail in RISC OS’s favour in the design stakes is the high-quality anti-aliasing technology that has been a part of the operating system since 1989. The renderer is designed, as are some of the more recently designed competing technologies available in other operating systems, to render type accurately at the cost of readability, but frankly, even at the 640×480 resolution I have been using, the typefaces still look clean and legible, which helps make the interface look clean and stylish.

screen_20121126_1s

RISC OS – stylish even at low resolutions, even better in high definition.

Enough about the style – how about the substance? It turns out that you get quite a few things even from your 6MB ROM image, including the full GUI environment, a text editor, a vector graphics program, a simple scientific calculator and a BBC BASIC interpreter.

Of course, it seems awfully odd and antediluvian to be supporting a BASIC interpreter in 2013, but BBC BASIC was one of the most sophisticated BASIC interpreters of its time and was extended with its move to RISC OS with capacity to write full, multitasking GUI applications. BBC BASIC is also one of the most optimised and rapid interpreted languages on any platform, proving sufficiently quick for the entire Arthur GUI interface to be written in it. The interpreter also includes capacity for inline ARM assembly language, providing a low-level programming environment inherent to the system. Few other operating systems actually have any inherent capacity for programming, and while Linux, Mac OS X and other Unix and Unix-like operating systems typically have programmability through their command shell, this isn’t going to fit in 6MB along with a GUI environment.

Unfortunately, when it comes to other applications, RISC OS currently looks a bit sparse. Given that the operating system has been maintained by a single, small company and kept alive mainly by hobbyists, this is to be expected, but you’re certainly not going to have the wealth of software that you have on Linux or Mac OS X, let alone Windows. This may improve if the community grows with the popularity of the Raspberry Pi, but it will prove difficult to use RISC OS for most serious work right now.

From a technical perspective, RISC OS is a very different beast to the three most popular desktop operating systems. Microsoft Windows comes from a lineage that incorporates elements of CP/M, OpenVMS and so on, while Mac OS X and Linux are obviously derived from Unix. RISC OS doesn’t derive from either lineage – or from any other apparent one either. Directory paths are delineated by full stops rather than slashes, for instance. Disc formatting uses the proprietary ADFS system first developed for the BBC Micro. Files don’t have extensions as default, with the file type determined by a six-byte file type number stored separately, and when extensions are used, perhaps from imported files from another operating system, the extension is delineated from the name by a forward slash.

One of the most distinctive details of RISC OS is how it deals with applications. Application names always begin with an exclamation mark, and RISC OS applications more closely represent directories in other operating systems than they do the executable files of Windows or Linux. In fact, RISC OS applications are extraordinarily modular in nature – you never have to “install” an application on RISC OS as you would in Windows, and you can just drag an application icon onto the icon bar to open it.

Another particularly distinctive detail of RISC OS comes from the way it handles the mouse. Acorn designed the Archimedes with a three-button mouse from the very start, and each of the buttons on the mouse have very individual functions. Unlike Windows, Mac OS X or Linux – or most other desktop GUI systems – RISC OS has a separate Menu button set to the middle button, and therefore, applications are not expected to have a program-specific menu bar, or a Ribbon interface or anything like that. The middle button performs menu tasks in every application, including the ones normally done by the right mouse button in Windows or Linux.

The other two button functions are Select, set to the left mouse button and performing tasks similar to the left mouse button in other desktop operating systems, and Adjust, set to the right mouse button. Adjust performs various functions, ranging from an alternate way to perform various tasks in most programs to an alternate menu for some application icons.

There are some places where RISC OS betrays its Eighties origin, though, and not necessarily in a good way. RISC OS uses cooperative multitasking rather than the pre-emptive multitasking common in operating systems from Unix to Microsoft Windows to AmigaOS and others besides. I have, in the past, been quite disparaging about the use of cooperative multitasking in any operating system, including RISC OS, and using RISC OS, it’s clear that it is an underlying disadvantage of the system.

I’m quite fond of pushing my systems to the limit when it comes to multitasking – it’s common for me to have a web browser, a word processor, a music player, a PDF reader and the file manager for my operating system all open at one time, with other tasks perhaps happening in the background. With a pre-emptive multitasking system, the programs are given a fair share of the computer’s free time, only occasionally locking up because one task is a bit too greedy with the clock cycles. With a cooperative multitasking system, it’s more difficult to run multiple applications at once, since one program that is badly designed or simply resource-heavy can lock up the system until it resolves. Using RISC OS for multimedia applications at the same time as performing a processor-heavy task is therefore a potential no-go area, which is a pity considering how smoothly the system runs on a single task.

Mostly, though, I like how different RISC OS 5 feels to other contemporary operating systems. Certain technical details, such as the obsolete cooperative multitasking model, make it difficult to recommend for everyday use right now, while the relative lack of applications also works against it. However, being allied to the Raspberry Pi could well give RISC OS a renewed lease of life, especially in the educational sector where it would be perfect for demonstrating that not every operating system is, or even has to be, the same as Windows or Mac OS X. In that sense, the OS could come full circle – from its educational roots right back around to them again.

SimCity 2000 – A Retrospective Review

While EA scrambles to try to sort out the controversial debacle surrounding the latest entry in the SimCity series, my mind has been cast back to the one previous entry in the series that I’ve played. SimCity 2000 came at a time when Maxis were an independent company, free of influence from EA, and followed a series of relatively unsuccessful titles also bearing the Sim branding. First released in 1994 on DOS, Mac OS and the Commodore Amiga, it spread eventually to a number of other computer and console platforms, including Windows 95, the SNES and the PlayStation.

The premise of the SimCity games has always been simple – keeping within your budget, attempt to build a successful city with a satisfied population, maintaining services and entertainment while trying to avoid disasters such as fires, riots or hurricanes. That is, unless you possess a certain sadistic streak, in which case you can build up a city just to watch it burn to the ground.

In order to build up the city, you must appropriately place zones of residential, industrial and commercial land in line with population demand. Low-population cities will desire industrial property, while higher populations desire more commercial property. There are two grades of each zone, light and dense: Light zones grow more quickly, but cannot hold as much population per tile; dense zones grow less quickly and tend to have a greater amount of crime and pollution per tile, but can support a greater population per tile.

Producing electrical power for the city is another imperative. Electrical demands start at a low level, capable of being satisfied by a single coal or oil power plant, but grow as the city grows and as time progresses further on. The options for power plants start with pollution-producing coal and oil power plants, along with expensive hydroelectric plants with a limited ability for placement, but expand with time to encompass natural gas stations, nuclear power plants and wind turbines, among others. Population zones are connected to power plants using power lines, and while it might be tempting to place a pollution-spewing coal plant far out of the way of your burgeoning city, every additional tile of power lines reduces the output at the business end of the power grid in an attempt to simulate power loss through thermal radiation through the power lines.

All of the population zones must then be connected with an appropriate transport system. Your options begin with basic roads and railways, but later expand to include subways and highways. Zone placement plays a large part in whether a transport system will be considered successful; the closer that a target zone is to the source zone of a denizen of the city, also known as a Sim, the less time and more satisfactory the journey will be for the Sim. However, roads can soon become clogged with too much traffic, making for logjams which make for dissatisfaction and an ultimate decrease in population.

As your city grows beyond a certain population, your Sims will begin to demand other facilities. Some of these are intended to lower the instances of potentially ravaging disasters, such as the police and fire stations, while others are designed to increase various components of your Sims’ satisfaction, including the health-improving hospital, the education-improving schools, colleges, museums and libraries and the recreational parks, zoos and stadiums.

A couple of other building zones exist beyond the residential, industrial and commercial zones, which become more available as your population grows. The seaport and airport both act to improve your city’s fortunes, creating new potential for industry and commerce when they are built. However, seaports can only be placed on bodies of water defined as rivers or coasts, while airport placement requires a certain amount of space devoid of dense buildings in order to prevent crashes which can cause devastating fires. However, the limitations don’t really get too much in the way of what are largely beneficial additions to your city.

The growth in population of your city comes with its own satisfaction, but a number of incentives become available once you reach various population milestones. The first of these, the Mayor’s House, becomes available at a population of 2,000, while other buildings become available at populations of 10,000, 30,000 and so on. One of these incentives, which comes at a population of 60,000, is optional. The military base available at this point can further increase your city’s population and gives you a small number of military units that can be used during disasters to both conquer fires and population uprisings, but leads to an increase in crime around the area that the military base is placed.

One of the major difficulties in building a large city is the limitation which is provided by your budget. You get a certain amount of money to begin with, at most $20,000, and if you pick the hard budgetary option, you end up with a $10,000 bond which accrues interest every year. With this money, you must produce a city that is self-sustainable enough to generate money so that city improvements can continue in the years going by. A property tax takes money from your populated zones, while various services including the police and fire forces, along with education, hospitals and transit cost money. It can be a difficult balance between maintaining satisfaction among your population and keeping within budgetary lines, and that’s before you get to various city ordinances, most of which have minor effects on how the city develops, but which generally cost extra money. If you get stuck, a small bond can be issued to give you a small period of solvency, but at the cost of interest.

A few evolutionary changes distinctively marked SimCity 2000 from its predecessor, including the isometric perspective replacing the top-down graphics of SimCity, and a greater granularity in terms of zone placement which allowed a single tile to be filled with a residential, commercial or industrial zone. Other changes added extra features, such as the inclusion of new transit and power plant options.

SimCity 2000 might seem like a somewhat arcane game with all of the options available to the player, but the beauty of the game is that the game is still easy to pick up while also maintaining its long-term potential. Indeed, SimCity 2000 was one of the first games that I played which I really felt compelled to continue playing. OK, there were some uninspired design decisions on the Windows 95 version that I first played; it took me a couple of weeks when I was playing the demo to realise that you had to hold down the toolbar buttons to get extra options, while the lack of ability to use the mouse wheel to scroll or zoom makes the game feel a little more clunky today than I’d like it to. That doesn’t spoil the core of the game, though – it still feels fun and challenging.

The graphics, as mentioned above, are isometric, so the game is going to look inherently dated compared to modern graphics. That said, there’s a difference between dated graphics and bad graphics and the sprites in SimCity 2000 are still reasonable today. All in all, the graphics suffice for their purpose, although they’re not exactly dazzling.

Similarly, the sound effects in the game serve their purpose, but aren’t going to blow your mind. The “electro-zap” effect of power plant and line placement does get somewhat repetitive, though. The music, on the other hand, is a funky collection of jazz-inspired MIDI tracks. Probably the best thing that can be said about the tracks is that they’re definitely distinctive and, to me at least, memorable; even sixteen years after I first played the game, a few bars of any of the tunes in the game evokes memories of the hours I’ve spent playing the game.

Despite the age of SimCity 2000, there’s very little that I can fault with it, at least on the platforms that could handle it with ease – specifically, the personal computer platforms such as Windows 95 and Mac OS Classic. The interface may feel a bit clunky at times on the DOS and Windows 95 platforms which I have played the most hours on, but this is a small price to pay for a game with such depth. There is, however, a more serious technical issue with the Windows version that betrays the game’s age: The game binary is 16-bit, completely preventing it from being played on a 64-bit version of Windows. Unless you have an older computer lying around with Windows XP or an earlier version, or unless you have a virtual machine on your computer with an earlier version of Windows, you’ll have to resort to using DOSBox in order to run the game, which isn’t as friendly an option.

Bottom Line: SimCity 2000‘s gameplay is still solid, even a couple of decades after its first release. Personal computer versions are preferable – the SNES version, in particular, feels distinctly limited – but beware the 16-bit binary on Windows versions.

Recommendation: It’s going to be difficult to find a working copy of SimCity 2000 for the personal computer platforms; even in 1997, when I bought it, it was in the bargain bin. I’d expect greater difficulty getting it today, especially considering that its sequel, SimCity 3000, has very much displaced it in the bargain bin section. If you can track it down, though, it’s well worth a bash.

Fundamentals of String Manipulation in C: Part 2

Having discussed most of the important functions relating to strings before, there are only a few others of particular note. We saw gets() previously, which acts like scanf(“%s”, string), and while it performs the job it’s asked to do, it is regarded as somewhat dangerous as it is prone to buffer overflow. A function does exist in the C standard library in <stdio.h> which is somewhat safer.

fgets() is an equivalent function to gets() designed to work on file input. Unlike gets(), you can specify a maximum number of characters to be taken in, which mitigates the buffer overflow that can occur with gets(). fgets() is called with three arguments: the string where input will be stored; the maximum number of characters, including the null character and either stdin, the reference of the standard input stream, or a pointer to a variable of the type FILE. File pointers will be discussed later; for now, we are only interested in stdin.

An example of the use of fgets() is illustrated below:

#include <stdio.h>

int main(void)
{
    char string[30];
    printf("Please enter a string: ");
    fgets(string, 30, stdin);
    puts(string);
    return 0;
}

One peculiar difference between fgets() and gets() is that fgets() does not remove newline characters from its input, while gets() does. This is to be noted when trying to concatenate two strings with strcat() which have been entered from the standard input stream with fgets().

Previously, we also saw the strcmp() function for comparing two strings. A similar function, strstr() (for string string) can be used to find an instance of a string of smaller or equal size within another string. It takes two arguments, both of them strings, and returns a pointer to the first instance of the string being searched for in the string being searched. The following program demonstrates strstr() in action.

#include <stdio.h>
#include <string.h>

int main(void)
{
    char string[] = "yellow dinosaurs eat snow reluctantly";
    char *p;
    int index;

    /* Looking for the location of "eat" in string[] */
    p = strstr(string, "eat");
    /* Finding the element within the array where "eat" begins */
    index = p - string;
    printf("The string \"eat\" begins at index %d of string[]\n");

  return 0;
}

This returns the following:

The string "eat" begins at index 16 of string[]

Searching for multiple instances of strings using strstr() requires a slight modification of our program. We can do this by creating an index, or a point in the array where the last instance of the string was found, and call the strstr() function from the next contiguous point in memory (i.e. the pointer string + index + 1). This example will benefit from the following illustration:

#include <stdio.h>
#include <string.h>

int main(void)
{
    char long_string[] = "The C programming language was invented in the \n"
    "early 1970s by the computer scientist, Dennis Ritchie, who was then \n"
    "working at Bell Labs in New Jersey, which had just removed its \n"
    "support from the Multics project.\n";
    int index = -1, count = 0, i;
    char *p;

    printf("%s", long_string);

    for (i = 0; i < strlen(long_string); i += index) {
	p = strstr(long_string + index + 1, "in");
	if ((index = p - long_string) >= strlen(long_string) || p == NULL)
	break;
	++count;
    }
    printf("\nThe string \"in\" has been located in the string %d times\n", count);
    return 0;
}

This example returns the following:

The C programming language was invented in the 
early 1970s by the computer scientist, Dennis Ritchie, who was then 
working at Bell Labs in New Jersey, which had just removed its 
support from the Multics project.

The string "in" has been located in the string 5 times

One thing to notice about this program is that the counter variable is not incremented by 1 on every loop, but instead by the value of index; this ensures that the loop continues only as long as there are still instances of the string being searched for to be counted.

In the last tutorial, We demonstrated atoi(), a function which converts a string consisting of numeral digits into a decimal integer. It was mentioned at the time that other functions of the same type exist. atof(), for instance, converts a string consisting of numeral digits, exponents and at most one radix point into a double; atol() operates like atoi(), except that it returns a long int. On most modern compilers, atol() works exactly like atoi(), but on older compilers with 2-byte ints, the two functions work differently.

The implementation of simple versions of these two functions is discussed in The C Programming Language (Kernighan & Ritchie, 2nd Edition, 1988). Other functions of this type with more flexibility exist, like strtod(), strtol() and strtoul(), the operations of which can be found in any good C reference material.

Hostile Space – A Preliminary Attempt at Tabletop Game Design

Editor’s Note: I’m back after a month’s break – trying among other things to consider an appropriate topic for a new blog post!

A few months back, I was ruminating on a discussion I’d had with my friends regarding game design. Game design is a field which always seems easy when first encountered, but many hidden issues show themselves after a deeper look. Many of these issues concern the concept of game balance, whereby one player has an insurmountable advantage (or at least an advantage that is difficult to surmount) over the opposing player. Occasionally, it’s a fun exercise to try to beat the odds against you, but if the game gives a consistent advantage to certain strategies or choices to the detriment of others, it makes a lot of the game designers’ efforts redundant.

Keeping this in mind, I set myself the task of creating a game that had limited complexity in terms of setting up the game, but with the potential for complex strategies and rules once the game began. As a template, I used a concept that I had conceived of for a computer game that I wish to write; as a result, I could get a twofer where I could test the rules with human players before trying to transfer it to the computer screen. The game uses a quasi-Newtonian concept of movement, similar but probably inferior to the likes of Triplanetary. Nevertheless, if I can figure out how to refine these rules, I will endeavour to do so.

I’m making these rules available under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License; specific details are available in the copyright details underneath the rules themselves. Any suggestions for rules changes or updates would be appreciated.

HOSTILE SPACE – A game for 2 or more players
—–
:Setting Up:

Each player begins with a set number of spacecraft (probably 4-8 in total). Each spacecraft, in turn, starts with a set number of missiles (probably 8) and a set delta-v value (probably 12-24 hexes).

The players throw a die to determine turn precedence. If two or more players receive the same amount on their die roll, these players roll again, and the order of these players is determined by this roll. The player that rolled the highest may pick a hex within the grid and place a marker there. The mark denotes an initial “zone of control” for that player, whereby the player may place their spacecraft anywhere within six hexes of that marker, and no other player may place a marker so that its zone of control would overlap that player’s zone of control. This process continues in descending order of the die rolls.

Once the spacecraft are placed, the players should remove their markers and mark their spacecraft numerically in the order they were placed. All missiles should in turn be marked with the number of the spacecraft which fired them followed by the number identifying the order in which they were fired.

—–
:Game Rules:

Once all spacecraft have been placed and numbered appropriately, the game begins. In the first phase of each turn, the players may adjust the velocities of each of their spacecraft. Each spacecraft has a set beginning delta-v value, which determines how much they may change their velocity.

An increase or decrease in velocity in either the direction that a spacecraft is pointing or the direction opposite to the one in which the spacecraft is pointing (a “linear change” in velocity) can be resolved by subtracting the value of the difference between the number of hexes that the spacecraft would move otherwise and the number of hexes the player wishes the spacecraft to move.

For instance, if the spacecraft is travelling at a velocity of four hexes in a given direction and the player wishes the spacecraft to only travel at a velocity of two hexes in that direction, the player will spend two hexes of delta-v to decelerate the spacecraft. This may be calculated as such:

[current] 4 hexes – [proposed] 2 hexes = [delta-v cost] 2 hexes.

This rule is also applied to linear changes in velocity that would result in the spacecraft travelling in the direction opposite to the one that it is pointing. For instance, if the spacecraft is travelling with a velocity of three hexes in the direction that it is pointing, and the player wishes it to have a velocity of three hexes in the direction opposite to the direction of travel, the player will spend three hexes of delta-v to bring the velocity to zero, then three more hexes to accelerate the spacecraft in the other direction. This may be calculated as such:

[current] 3 hexes – [proposed] (-3 hexes) = [delta-v cost] 6 hexes.

If the velocity of a spacecraft is zero, the player may change the direction in which it is facing for no cost. If, however, the velocity of the spacecraft is non-zero, a change in direction costs one hex for each hex between the place where the spacecraft would end its movement at the current velocity and the proposed target, calculated in a manner of “concentric circles”. [NOTE: I'll have to put in rules illustrating this better, if not completely rethink this section of the rules.] Calculate velocity change costs for linear velocity change first, then for directional change.

After all players have chosen their changes in velocity, the movement phase begins. Each player in order of turn precedence repositions their spacecraft in accordance with their new velocity. If at the end of movement, two spacecraft or a spacecraft and an object which occupies only a single hex are in the same hex, destroy both entities. If at the end of movement, a spacecraft is in the same hex as an object that occupies more than one hex, destroy the spacecraft. If during movement, a spacecraft would travel outside the borders, place the spacecraft at the corresponding position at the opposite side of the map and continue its movement in the current direction. [NOTE: This leads to a toroidal game area, but short of awkward rules concerning the "halfway point" of a hex grid, this is a necessary expedient.]

After the movement phase, the firing phase begins. Each player in order of turn precedence may choose to fire one missile from each of their spacecraft as long as that spacecraft has at least one missile remaining. The missile is placed in any of the hexes surrounding the spacecraft and will continue in that direction at a fixed velocity at least slightly higher than the maximum velocity of a spacecraft (approximately 15-30 hexes, based on proposed delta-v limits). Once all players have declared which spacecraft, if any, will fire, resolve the movement of each of the missiles, first by order of the players’ turn sequence, then by the order in which they were fired.

If, during movement, a single missile would pass through the hex occupied by a spacecraft or an entity occupying a single hex, destroy that missile and resolve damage if appropriate. An entity occupying a single hex is immediately destroyed, while damage to a spacecraft may be resolved under two systems: the “Brutal” system where a single hit from a missile will destroy a spacecraft, and the “Fortified” system where a spacecraft has three hit points, and on each hit from a missile, a D6 is rolled and the result modulo 3 is taken away from the spacecraft’s hit points.

If a missile would pass through a hex occupied by an entity occupying more than one hex, destroy that missile.

If, during movement, multiple missiles would pass through a hex occupied by a spacecraft or an entity occupying a single hex, determine the missile that was fired first, and on which turn it was fired. That missile, along with any others fired on the same turn, are considered to have hit and may resolve damage as normal. The remainder of the missiles pass through the hex and continue movement as normal.

The winner may be determined by the last remaining player, or by the player with the most spacecraft, or in the case of a tie, most missiles remaining after a number of turns.

—–
:Entity placement rules:

At the beginning of the game, the players may choose to place additional game entities such as asteroids on the game map. If they choose to do so, roll either one or two D6s to determine the number of additional entities to place.

The entities are then placed on the map after the players have positioned their spacecraft. Each entity is placed one at a time on the map by the players in order of turn precedence. An entity may be placed on any hex or series of hexes on the map as long as it does not occupy or overlap a hex that is already occupied and as long as it does not overlap a player’s zone of control established by their marker or base.

—–
:Base Defence – an alternate play style:

In the Base Defence play style, each player begins with a base, a game entity consisting of seven tokens: The central token, which occupies a single hex, and six peripheral tokens which occupy the hexes surrounding the central token. Instead of placing a marker at the start of the game, the players place their bases instead, and the central hex determines the centre of the player’s zone of control.

The objective in Base Defence is to destroy your opponent’s bases by attacking them with missiles and destroying the central token, while simultaneously defending your own. A player whose base is destroyed is considered to be defeated; any remaining base tokens, spacecraft and missiles owned by that player remain in play and all spacecraft and missiles continue at the velocity they were travelling. However, a defeated player may not change the velocity of any of their spacecraft, nor may any of their spacecraft fire any missiles.

A base does not follow the usual Hostile Space rules for entities occupying more than one hex. If a spacecraft or missile would pass through a hex occupied by a base token, both the spacecraft or missile and the base token is considered to be destroyed, and the hex occupied by the base token is then considered to be empty for the purposes of other objects passing through that hex. Once the central token of a base is destroyed, the player owning that base is defeated; see above for the consequences of defeat.

If a spacecraft ends its turn at zero velocity beside a base token owned by either the same player that owns that spacecraft or a player allied to the owner of the spacecraft, that spacecraft regains all of its expended delta-v and up to two of its expended missiles, effective on the next turn.

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.

More Raspberry Pi Electronics Experiments – Gertboard and Potentiometer-Controlled LED Cluster

One of the things which alerted me to the potential of the Raspberry Pi as an electronics control system was the announcement of the Gertboard before the Raspberry Pi was released into the market. When the Gertboard was announced for sale in November 2012, it was fully my intention to buy one, but a lack of money kept me from purchasing it at that point. The unassembled Gertboard kits soon sold out, leaving element14, who distribute the Gertboard, to decide to release an assembled Gertboard. This went on sale very recently, and shortly after release, I bought one from Premier Farnell’s Irish subsidiary.

The Gertboard, for the uninitiated, is an I/O expansion board that plugs into the GPIO header on the Raspberry Pi. Designed by Gert van Loo, designer of the Raspberry Pi alpha hardware, the Gertboard not only protects the GPIO pins from physical and electrical damage, but also provides a set of additional features. These include input/output buffers, a motor controller, an open collector driver, an MCP3002 ADC and MCP4802 DAC and an Atmel ATMega328P microcontroller which is compatible with the Arduino IDE and programming environment.

I was very impressed by the quick response from element14 after my purchase; my delivery came only two days after ordering and would have come even sooner if I hadn’t missed the 20.00 deadline on the day I had ordered it. The Gertboard was packaged with a number of female-to-female jumper wires, a set of jumpers, plastic feet for the board and a CD-ROM with a set of development tools for the ARM Cortex-M platform.

gertboard_package

So far, I’ve only had occasion to test the buffered I/O, the ADC and DAC and the microcontroller; I still don’t have parts to test the motor controller or open collector driver. Aside from some documented peculiarities regarding the input buffers when at a floating voltage, including the so-called “proximity sensor” effect, things seem to have been going rather well.

The acquisition of the Gertboard gave me the impetus to really get down to trying to test my own expansions to the simple test circuits I had implemented before. One interesting application that I considered was to use a potentiometer to control a bank of LEDs in order to provide some sort of status indication.

The following Fritzing circuit diagram shows the layout of this circuit without the use of the Gertboard; the onboard LEDs and GPIO pins lined up in a row on the Gertboard makes it slightly less messy in terms of wiring.

Potentiometer Controlled LEDs_bb

In this diagram, GPIO pins 0, 1, 4, 17, 18, 21, 22 and 23 are used to control the LEDs, although you could also use pins 24 or 25 without conflict with either the SPI bus – which is necessary for the MCP3002 ADC – or the serial UART on pins 14 and 15. However, this is a lot of GPIO pins taken up for one application, which may warrant the use of a shift register or an I2C I/O expander such as the MCP23008 or MCP23017 in order to control more LEDs with less pins.

In order to control this circuit, I took the sample Gertboard test software and modified it slightly. As the potentiometer is turned to the right, the ADC value increases to a maximum of 1023; therefore, the distance between each LED’s activation point should be 1023 divided by 8 – very close to 128. The LEDs will light from left-to-right as the potentiometer’s resistance decreases, with one LED lighting at an ADC reading of 0, two LEDs at 128, all the way up to all eight LEDs at 1023.

//
// Gertboard Demo
//
// SPI (ADC/DAC) control code
//
// This code is part of the Gertboard test suite
//
//
// Copyright (C) Gert Jan van Loo & Myra VanInwegen 2012
// No rights reserved
// You may treat this program as if it was in the public domain
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//

#include "gb_common.h"
#include "gb_spi.h"

void setup_gpio(void);

int leds[] = {1 << 23, 1 << 22, 1 << 21, 1 << 18, 1 << 17, 1 << 4, 1 << 1,
	      1 << 0};

int main(void)
{
    int r, v, s, i, chan, nleds;

    do {
	printf("Which channel do you want to test? Type 0 or 1.\n");
	chan = (int) getchar();
	(void) getchar();
    } while (chan != '0' && chan != '1');

    printf("When ready, press Enter.");
    (void) getchar();

    setup_io();
    setup_gpio();
    setup_spi();

    for (r = 0; r < 1000000; r++) {
	v = read_adc(chan);
	for (i = 0; i < 8; i++) {
	    GPIO_CLR0 = leds[i];
	}
	nleds = v / (1023 / 8); /* number of LEDs to turn on */
	for (i = 0; i < nleds; i++) {
	    GPIO_SET0 = leds[i];
	}
	short_wait();
    }

    printf("\n");
    restore_io();
    return 0;
}

void setup_gpio()
{
    /* Setup alternate functions of SPI bus pins and SPI chip select A */
    INP_GPIO(8); SET_GPIO_ALT(8, 0);
    INP_GPIO(9); SET_GPIO_ALT(9, 0);
    INP_GPIO(10); SET_GPIO_ALT(10, 0);
    INP_GPIO(11); SET_GPIO_ALT(11, 0);
    /* Setup LED GPIO pins */
    INP_GPIO(23); OUT_GPIO(23);
    INP_GPIO(22); OUT_GPIO(22);
    INP_GPIO(21); OUT_GPIO(21);
    INP_GPIO(18); OUT_GPIO(18);
    INP_GPIO(17); OUT_GPIO(17);
    INP_GPIO(4); OUT_GPIO(4);
    INP_GPIO(1); OUT_GPIO(1);
    INP_GPIO(0); OUT_GPIO(0);
}
Follow

Get every new post delivered to your Inbox.