分类:
2009-06-29 21:21:33
In this lab, you will learn how to compile C programs for the MSP430 microcontroller (often called an MCU, or a microcontroller unit). The following program turns on an LED wired to the least significant bit of an i/o port called "p1" mapped to memory address 0x21:
int main (void) { char* p1out; /* Get a reference to output port 1 */ p1out = (char*) 0x0021; /* Turn on the LED */ *p1out = 0x01; for(;;) // loop forever ; // no such thing as "exiting" from an embedded controller }
The primary purpose of this lab is to introduce the tools used to cross-compile programs for the MSP-430 and to load them into memory.
Note that a program development tool such as a compiler, assembler, debugger, etc. is just a program that can be potetially compiled for any computer. For example,the C compiler gcc you have been using that generates pentium code has been compiled to run on the pentium computers in our lab. We have instaled a full set of "cross tools" that run on our lab's pentium computers that generate and manipulate MSP-430 code. For convenience, we named them all with a MSP-430 prefix. For example:
Native tool name | Cross tool name |
gcc | msp430-gcc |
as | msp430-as |
gdb | msp430-gdb |
objdump | msp430-objdump |
nm | msp430-nm |
Using these tools, you can compile led.c to an msp430 "elf" file (elf is a binary file format) as follows:
$ ls led.c $ msp430-gcc -g led.c -c -o led.o $ ls led.c led.o $ msp430-gcc led.o -o led.elf $ ls led.c led.elf led.o
Use the program objdump to disassemble the code in led.elf using the following command to determine the instructions that set the least significant bit of port1, which should be a few instructions after the symbol main. Note the addresses of these instructions.
$ msp430-objdump --disassemble led.elf
The ez-430's are USB dongles including a USB port that allows them to be connected to a computer. To get ready to load a program to the MSP430, there are a couple of steps you need to take:
$ msp430-run-gdbproxy
Back in our old terminal, let's now run gdb to debug our program:
$ msp430-gdb led.elf GNU gdb 5.1.1 Copyright 2002 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "--host=i686-pc-linux-gnu --target=msp430". (gdb)
In order to get gdb to speak to the "MSP430" target via USB, you must give it a few configuration commands:
(gdb) setremoteaddresssize 64 (gdb) setremotetimeout 999999 (gdb) target remote localhost:2000 Remote debugging using localhost:2000 0x0000fc00 in ?? ()
The "monitor erase" command erases the MSP-430's flash memory:
(gdb) monitor erase all Erasing target flash - all... Erased OK (gdb)
The "load" command tells gdb to load the MSP-430's memory with the program:
(gdb) load Loading section .text, size 0x58 lma 0xfc00 Loading section .vectors, size 0x20 lma 0xffe0 Start address 0xfc00, load size 120 Transfer rate: 960 bits in <1 sec, 30 bytes/write. (gdb)
Gdb thinks that the MSP-430 is a paused program, so to run it, you can tell gdb to permit the proram to continue executing:
(gdb) c Continuing.
And now, the LED should be lit! (call your TA if it is not). You can tell GDB to take command again by typing a control-c (which should stop the program being executed).
Your assignment is to familiarize yourself with the commands discussed in this lab and to modify led.c to flash the LED on and off. A couple of hints:
2-0: Introduction to C for the MSP430 |
.arch msp430x110 .p2align 1,0 .text main: .globl main ; Set up port 1 for output mov.b #1, &0x0022 on: ; Turn on the LED mov.b #1, &0x0021 ; Loop forever loop: jmp loop off: ; Turn off the LED mov.b #0, &0x0021
$ msp430-as led.s -o led.o
$ msp430-gcc led.o -o led.elf
; Sleep a while mov #0xffff, r4 sleep: cmp #1, r4 jeq done ; jump to done after we're done waiting sub #1, r4 jmp sleep
. . . --- --- --- . . .
In this lab, you will implement a program that flashes "SOS" with the following timing:
To do this, you will:
void millisleep(unsigned short delay); // no sense in negative delays!That loops for delay milliseconds, and then returns. For example:
millisleep(500);Should delay for 1/2 second.
Prior to building this lab, you should assess the complexity and inter-dependency of each of these tasks and concisely document (in your README) the sequence that you will implement features and manner that you will test them. You MUST have a TA agree to this sequence prior to implementation. Your final README should also indicate the effectiveness of this strategy.
In this assignment, students will construct a subroutine and all needed helper-functions to implement
char print_serial(char c)
That
This function may be written in C or assembly language, though it probably would be simpler to write in C with helper functions in assembly.
It is acceptable to transmit characters at any common RS-232 format and baud rate, but we recommend:
A frame of an rs232 transmission is as follows:
Each frame consists of the start bit 1, followed by 7 bits of the messages, which are inverted, followed by the stop bit of 0.
Like the previous lab, students will be required to prepare and submit a design and test plan. We encourage a strategy where components are tested before integration. You may want to test the function that converts characters to RS-232 bit sequences using a native development environment so that you have access to console I/O. For example, you may want to print diagnostics:
printing character 'A' = 0x41 bits: (start) 1 0 1 0 0 0 0 0 1 (stop) 0
If you follow this approach, you may want to put a copy of this test environment in a subdirectory with its own Makefile and document it in your README.
Useful advice: The serial port line that goes from the MSP430 to the computer's serial port is at P1.7 on the MSP430. (The LED is connected to P1.0.)
Note: You must test your routine using a serial terminal emulator. On the lab systems, we use minicom. You can find instructions for using minicom in .
In this assignment, we will provide you with interrupt-driven code to send enqueued characters to the serial port. As shown below, the provided program sets up the serial subsystem, and then slowly (and repeatedly) enqueues "hello" followed by a carriage-return & newline.
The sample program contains:
As shown below, your program must add additional functionality to:
This function may be written in C or assembly language, though it probably would be simpler to write in C, possibly with helper functions in assembly.
Like previous labs, students will be required to prepare and submit a design and test plan. We encourage a strategy where components are tested before integration.
If you follow this approach, you may want to put a copy of this test environment in a subdirectory with its own Makefile and document it in your README.
Using struct in C
#include/* A struct representing a node in a linked list */ struct node { char value; struct node* next; }; /* Our main function */ int main (int argc, char** argv) { int i; struct node* node; /* Allocate memory for our six nodes */ char memory[6][sizeof (struct node)]; /* Create pointers to six nodes */ struct node* list[6]; /* Map the regions of memory we created above to our linked list */ for (i = 0; i < 6; i++) { /* Assign a chunk of memory to the struct pointer */ list[i] = (struct node*) memory[i]; } /* Set values for each of our nodes */ list[0]->value = 'a'; list[1]->value = 'b'; list[2]->value = 'c'; list[3]->value = 'd'; list[4]->value = 'e'; list[5]->value = 'f'; /* Make each node point to the node after it */ for (i = 0; i < 5; i++) { /* Make this node point to the one after it */ list[i]->next = list[i + 1]; } /* Null-terminate our linked list */ list[5]->next = (void*) 0; /* Begin at a, our head node */ node = list[0]; /* Loop through each node until we've reached the end of the list */ for (i = 0; node != (void*) 0; i++) { /* Print this node's value */ putchar (node->value); putchar ('\n'); /* Go to the next node */ node = node->next; } return 0; }
Suggest waiting 1s after reset for bounce to stop. Good solutions should work multiple times (clearing the count) from a single load of the program.
In this lab, you will learn how to compile C programs for the HC11 microcontroller (often called an MCU, or a microcontroller unit) in the robots. But before we start with the robots, let's take a few minutes to look at this example program:
int main (void) { char* porta; /* Get a reference to PORTA */ porta = (char*) 0x1000; /* Turn on the LED. On my robot, the LED is connected to the pins at * 0x10 on PORTA, but yours is probably different. */ *porta = 0x10; return 0; }
What does this program do, exactly? As you can see, it simply stores the value 0x10 at address 0x1000 in memory. On my robot, this is how you turn the LED on. But first, what would happen if we tried to run this program on a standard x86 computer? Let's try it:
$ gcc simple.c -o simple $ ./simple Segmentation fault (core dumped)
That's interesting. Why did we get Segmentation fault (core dumped) instead of no output, like we expected? The answer is relatively simple. On x86 systems running an operating system (in this case, Cygwin emulates a Linux-like operating system), when a program is executed, it is assigned a memory space by the operating system. When the program attempts to access memory locations outside of the range assigned to it by the operating system, the result, on UNIX systems, is to generate a signal called SIGSEGV. Signals are commonly used on UNIX systems to handle external conditions affecting the execution of programs. In this case, the program receives SIGSEGV from the operating system (Cygwin) and must terminate immediately. The shell (bash) detects that the process terminated with the SIGSEGV signal and prints the message Segmentation fault (core dumped).
On an x86 system, of course, it is extremely unlikely that the operating system would assign a process a range of memory that includes the address 0x1000. This is why running our program under Cygwin gives us a segmentation fault.
So what exactly is different between the way the HC11 microcontroller and standard x86 systems manage memory? Simple: the operating system. The simple robots do not run any operating system at all. When we compile and run programs on the robots, we have full access to the entire 65,536-byte memory addressing space of the HC11. On the x86 systems, on the other hand, the operating system prevents us from accessing any regions of memory that aren't strictly ours.
You may not know it, but there are actually many steps required to turn your C source code into an executable. The first step is, no doubt, the one that you're most familiar with: compiling. Let's dig a little deeper into the stages of compiling.
When you run gcc, the first step is running the code through the C preprocessor. Are you familiar with the commands that start with # in C, like #define, #include, and so on? These are called preprocessor directives. Let's run a simple demonstration program through the preprocessor to see what comes out; to do this, we use the -E switch with gcc:
#includeint main (int argc, char** argv) { puts ("Hello, world!"); return 0; }
$ gcc -E hello.c # 1 "hello.c" # 1 "" # 1 " " # 1 "hello.c" # 1 "/usr/include/stdio.h" 1 3 4 # 28 "/usr/include/stdio.h" 3 4 # 1 "/usr/include/features.h" 1 3 4 ... # 842 "/usr/include/stdio.h" 3 4 # 2 "hello.c" 2 int main (int argc, char** argv) { puts ("Hello, world!"); return 0; }
As you can see, the preprocessor added quite a few lines to the simple hello.c. What do all of these lines do? That's beyond the scope of this course. Just know that they've been added because of the #include statement at the top of the code.
For now, though, let's take a look at the second stage of compiling: the conversion from C to assembly, which is actually called compiling. Let's use the -S switch with gcc on our hello.c to see what kind of assembly output gets produced (we use the -o - switch to write the output to the standard output):
$ gcc -S hello.c -o - .file "hello.c" .section .rodata .LC0: .string "Hello, world!" .text .globl main .type main, @function main: leal 4(%esp), %ecx andl $-16, %esp pushl -4(%ecx) pushl %ebp movl %esp, %ebp pushl %ecx subl $4, %esp movl $.LC0, (%esp) call puts movl $0, %eax addl $4, %esp popl %ecx popl %ebp leal -4(%ecx), %esp ret .size main, .-main .ident "GCC: (GNU) 4.1.2 (Gentoo 4.1.2)" .section .note.GNU-stack,"",@progbits
As you can see, gcc produced some assembly code. Of course, this is x86 assembly code, which you are not expected to understand. Just know that this small fragment of assembly code, when converted to machine code and executed on an x86 CPU, will print Hello, world! to the screen.
This leads us to the final stage of compiling: assembly. To execute anything on a CPU, we first need to convert our program to machine code. This is exactly what the assembly stage of compiling does. To generate a file containing the binary machine code produced by assembling the assembly code above, let's use the -c switch with gcc, followed by running a utility called objcopy:
$ gcc -c hello.c -o hello.o $ objcopy -j .text -O binary hello.o hello.bin $ hexdump -C hello.bin 00000000 8d 4c 24 04 83 e4 f0 ff 71 fc 55 89 e5 51 83 ec |.L$.....q.U..Q..| 00000000 8d 4c 24 04 83 e4 f0 ff 71 fc 55 89 e5 51 83 ec |.L$.....q.U..Q..| 00000010 04 c7 04 24 00 00 00 00 e8 fc ff ff ff b8 00 00 |...$............| 00000010 04 c7 04 24 00 00 00 00 e8 fc ff ff ff b8 00 00 |...$............| 00000020 00 00 83 c4 04 59 5d 8d 61 fc c3 |.....Y].a..| 00000020 00 00 83 c4 04 59 5d 8d 61 fc c3 |.....Y].a..| 0000002b
And there you have it, the exact binary code that the x86 CPU can understand. Feel free to take a look at hello.bin with a text editor if you're curious. You'll just see control characters that are interpreted as opcodes by the CPU. Of course, we have no way to actually execute this program in its binary format because we're running in an operating system. Because the operating system needs a format for executables that it can understand, we have the ELF format. On Linux systems, this is the standard executable format. (An in-depth description of the ELF format is well beyond the scope of this course. However, if you're interested in learning more about it, you are encouraged to research it yourself.) In fact, the hello.o file you created in the last step, called an object file, is in ELF format. Let's take a look at what it contains using the objdump utility:
$ objdump -s hello.o hello.o: file format elf32-i386 Contents of section .text: 0000 8d4c2404 83e4f0ff 71fc5589 e55183ec .L$.....q.U..Q.. 0010 04c70424 00000000 e8fcffff ffb80000 ...$............ 0020 000083c4 04595d8d 61fcc3 .....Y].a.. Contents of section .rodata: 0000 48656c6c 6f2c2077 6f726c64 2100 Hello, world!. Contents of section .comment: 0000 00474343 3a202847 4e552920 342e312e .GCC: (GNU) 4.1. 0010 32202847 656e746f 6f20342e 312e3229 2 (Gentoo 4.1.2) 0020 00 .
That probably doesn't look like much to you. However, there are a couple of things you can see immediately. First of all, you can see that hello.o is divided into three sections: .text, .rodata, and .comment. You can also see that each of these sections contains some data. One thing to note, however, is that the HC11 will not use the .rodata section ("read-only data"); instead, it simply uses the .data section. Here is a brief summary of each of the sections you will encounter and what they are used for:
Now suppose we'd like to see a list of the routines contained in a compiled object file. Of course, we know that we wrote a single function, main (), in our hello.c routine. But let's see a list of the symbols defined in hello.o using the nm utility:
$ nm hello.o 00000000 T main U puts
We can see a couple of things here. First, we can see that our main () routine is defined in the .text section (which is why it has T next to it) and that it resides at address 0x00000000 in our hello.o file. Next, we see that somewhere in our hello.o file, there is a reference to something called puts. Since it has a U next to it, we know that it is an undefined symbol. This means that hello.o has no idea what puts is, just that it needs to look for puts during our next build stage, linking.
Now you know how to create object files. But what do we do with all these object files? How do we put them together and make a single program? The answer, logically enough, is that we must link the object files into an executable. This process is typically called separate compilation, or more commonly, linking. Let's write a simple program to calculate the factorial of a number:
#includeint main (void) { long f; /* Determine 12! */ f = factorial (12); /* Print the result */ printf ("12! = %ld\n", f); return 0; }
long factorial (int n) { if (n == 0) { return 1; } else { return n * factorial (n - 1); } }
Now, let's compile these two files into object files and take a look at the symbols they use with nm:
$ gcc -c main.c -o main.o $ gcc -c factorial.c -o factorial.o $ nm main.o U factorial 00000000 T main U printf $ nm factorial.o 00000000 T factorial
From the output of nm, we can see that main.o has only the main () routine defined and that it contains references to two other routines (these are more generically known as symbols): factorial, and printf. For now, don't worry about the function of the printf () function. It is beyond the scope of this course. However, we can see that the factorial () routine is defined in our factorial.o object file. As you would probably guess, this is the same factorial () function we are calling from main (). So, are you ready to link the two object files together to get an executable? Let's use gcc to do our linking:
$ gcc -o factorial.elf main.o factorial.o $ ./factorial.elf 12! = 479001600
And there you have it, 12! = 479,001,600. At this point, hopefully you have a basic understanding of how we can link together multiple object files into a single executable. However, until now, everything we have been doing is for the x86 architecture, not for the HC11. The process is essentially the same for the HC11, but it does vary slightly. All the utilities you have been using (gcc, nm, objcopy, objdump, and so on) are also available for the HC11. The only difference is that you should prepend "m6811-elf-" to every command's name. That means you should use m6811-elf-gcc instead of gcc, for example.
Let's compile our LED program again, but this time for the HC11; we'll also take a look at the object file with nm, and then objdump:
int main (void) { char* porta; /* Get a reference to PORTA */ porta = (char*) 0x1000; /* Turn on the LED. On my robot, the LED is connected to the pins at * 0x10 on PORTA, but yours is probably different. */ *porta = 0x10; return 0; }
$ m6811-elf-gcc -c simple.c -o simple.o $ m6811-elf-nm simple.o U _.frame 00000000 T main $ m6811-elf-objdump -s simple.o simple.o: file format elf32-m68hc11 Contents of section .text: 0000 de003c3c 9f00cc10 00de00ed 0118de00 ..<<............ 0010 cdee01c6 10e7004f 5fce0000 18381838 .......O_....8.8 0020 18df0039 ...9 Contents of section .comment: 0000 00474343 3a202847 4e552920 332e332e .GCC: (GNU) 3.3. 0010 362d6d36 38686331 782d3230 30363031 6-m68hc1x-200601 0020 323200 22.
The next step, then, is to link our object file. But how will we link it? How does the linker know what it needs to do? To answer that, we need to take a look at a file called memory.x:
/* We want to generate code for the HC11 */ OUTPUT_ARCH(m68hc11) /* We want to generate an ELF binary for the HC11 */ OUTPUT_FORMAT(elf32-m68hc11) /* Our stack will begin at 0xffbf, just below the vectors. Remember, the stack * works upside down on the HC11 for some strange reason. That means it "grows * down," not "up," the traditional way. We want the stack to take up 0x100 * bytes, from 0xfebf to 0xffbf. */ _stack = 0xffbf; /* Begin executing at _start */ ENTRY(_start) /* Define regions of memory */ MEMORY { ram (rwx) : ORIGIN = 0x0000, LENGTH = 0x0100 mem (rx) : ORIGIN = 0x8000, LENGTH = 0x8000 stack (rw) : ORIGIN = 0xfebf, LENGTH = 0x0100 reset (rw) : ORIGIN = 0xfffe, LENGTH = 0x0002 } /* Define the sections of our output object file */ SECTIONS { /* Put page0 in RAM */ .page0 : { *(.page0) } > ram /* Set up virtual software registers in RAM */ .softregs : { *(.softregs) } > ram /* Save our data section */ .data : { *(.data) } > mem /* Next, our bss */ .bss : { *(.bss) } > mem /* And finally, code goes in the memory */ .text : { *(.text) } > mem /* Insert our reset vector at 0xfffe */ .vectors : { *(.vectors) } > reset }
You're not expected to understand anything in memory.x. The important thing is for you to understand that it tells the linker the following important things:
As mentioned above, the linker needs to know where to begin executing. This is the symbol called _start. But, as you may have noticed, we did not define any symbol with that name in our simple.c program. If we had, it would have showed up when we ran nm. To deal with this, we actually need to link in another object file, crt0.o. The purpose of this object file is simple: it defines the _start symbol, sets up the HC11 to prepare to execute your program, and then begins executing your program. crt0.o performs some other functions, as well. However, these aren't important for the time being. (In case you're curious, "crt" stands for "C runtime.")
Let's take this opportunity to explain something about how we linked the x86 sample program earlier. If you remember, we used gcc to perform the linking, providing the names of our object files as arguments. However, there is a special program called ld that is often used to link object files and is better suited to our purposes. The reason we didn't use ld in the previous x86 example is because for the x86 architecture, the dependencies for linking far outnumber those needed for the HC11 architecture. Therefore, we used gcc instead of ld in the example because it takes care of many of these problems automatically. However, for the HC11, since we have everything we need, specifically memory.x and crt0.o, we will be using ld to link object files.
So, to recap, we know we need to do two things before we can link object files for the HC11. First, we need to specify the correct memory.x file, so the linker knows how to link the object files. Second, we need to link in crt0.o so the _start symbol is defined. Just so you get a feel for how crt0.o works, let's try linking our simple.o, first without crt0.o, then with it; note that we must define the path to memory.x using the -T switch:
$ m6811-elf-ld -T /opt/m68hc1x-gcc/m6811-elf/lib/ldscripts/cs3432.x \ -o simple.elf simple.o m6811-elf-ld: warning: cannot find entry symbol _start; defaulting to 00008000 simple.o(.text+0x1): In function `main': : undefined reference to `_.frame' simple.o(.text+0x5): In function `main': : undefined reference to `_.frame' simple.o(.text+0xa): In function `main': : undefined reference to `_.frame' simple.o(.text+0xf): In function `main': : undefined reference to `_.frame' simple.o(.text+0x22): In function `main': : undefined reference to `_.frame' $ m6811-elf-ld -T /opt/m68hc1x-gcc/m6811-elf/lib/ldscripts/cs3432.x \ -o simple.elf \ /opt/m68hc1x-gcc/lib/gcc-lib/m6811-elf/3.3.6-m68hc1x-20060122/crt0.o \ simple.o
There are a couple of important things to note about this. Most obviously, these are extremely long and difficult-to-remember commands. Don't worry, though, because as you'll soon see, we don't expect you to remember these long commands.
The other important thing to note is the error output from the first command, without linking in crt0.o. You can see that _start is not defined. Without defining _start, programs will never execute properly on the HC11. The other error we see is that _.frame is not defined. _.frame is one of the soft registers that crt0.o defines (namely, the frame pointer). The function of _.frame isn't really important right now; just know that it's something that crt0.o provides so we can link properly.
You'll see now, if you execute ls, that there's a simple.elf executable. This executable, however, is built to run on the HC11, not the x86 architecture. Let's try to execute it, just to see the error that gets produced:
$ ./simple.elf ./simple.elf: ./simple.elf: cannot execute binary file
If you recall, as mentioned earlier, the ELF format is used by the operating system (Linux, for example) to execute programs from within the operating system. Obviously, since the HC11 has no operating system, the ELF format isn't very well suited to run on the HC11. Instead, we have to convert the ELF file to a format better suited to the HC11's minimal system constraints.
Motorola has developed a special format, called S19 format, that can be used to store executable code. It is comparable to the ELF format, but is a highly specialized format for use on the HC11. The specifics of the S19 format are beyond the scope of this course, but you are encouraged to research it yourself if you are interested.
Before we can try out our simple.c program, we need to convert the ELF file to an S19 file. To do this, we can use the objcopy utility:
$ m6811-elf-objcopy -O srec -j .text -j .data -j .bss -j .vectors simple.elf \ simple.s19 $ cat simple.s19 S00D000073696D706C652E7331395D S11380008EFFBFBF00004F06BD800D20FEDE003C8A S11380103C9F00CC1000DE00ED0118DE00CDEE0127 S1138020C610E7004F5FCE00001838183818DF007C S10480303912 S105FFFE80007D S90380007C
This command instructs the objcopy utility to convert the data contained in the ELF file to S19 format (called S records), keeping only the sections of the executable that are important. Now, the next step, of course, is to get the S19 file to the robot.
The robots, as you may know, can be connected to a computer. The robots have a serial communications interface (SCI) that allows the robot to be connected to a computer's serial port. The HC11 has a small program built into its ROM that allows you to upload a program to execute on the HC11. This sounds like a great way to load our program, but there is a major problem: this program in the HC11's ROM will only allow the uploading of programs up to 256 bytes in size.
Of course, this is a problem. Since the robots have 32 kilobytes of memory, it doesn't make very much sense that our program size is limited to 256 bytes. So, we found a clever way around this. We simply created a loader program (which is far below 256 bytes in size) that reads in your program, in S19 format, over the serial port, and stores it in the full 32-kilobyte memory space of the robot.
To get ready to load a program to the robot, there are a couple of steps you need to take:
Let's load our S19 file to the robot now (of course, replace /dev/com1 in the example with your proper COM port):
$ m6811load -p /dev/com1 simple.s19 m6811load version 1.0.1 Loaded 256-byte loader program /usr/share/m6811load/loader.bin Read 206 bytes from simple.s19 Found 51 bytes of executable code in S19 Converted simple.s19 to HC11 machine code Opened serial port /dev/com1 at 1200 baud Gave the download command to the HC11 Bootstrapping the loader program Error: Handshake failure: Sent CE but got 00 back
You can see that, for me, it didn't work this time. It's no problem, we just need to press the reset button on the robot and try again:
$ m6811load -p /dev/com1 simple.s19 m6811load version 1.0.1 Loaded 256-byte loader program /usr/share/m6811load/loader.bin Read 206 bytes from simple.s19 Found 51 bytes of executable code in S19 Converted simple.s19 to HC11 machine code Opened serial port /dev/com1 at 1200 baud Gave the download command to the HC11 Bootstrapping the loader program Bootstrapping finished successfully Serial port closed Opened serial port /dev/com1 at 9600 baud Gave the download command to the loader program Loader program is accepting data Loading finished successfully Serial port closed Finished. Now reset the HC11 in expanded mode to run your program.
There we go. After a brief delay, the program was loaded with no problem. Now, let's run the program. Move the left switch to the "Run" position and press the reset button.
Nothing happened, right? Well, this is your assignment for Lab 2.10. You need to figure out which pins on PORTA your robot's LED is connected to. On my robot, the LED is connected to 0x10, but it's probably different on your robot, so you'll need to figure which value (instead of 0x10) to use to get your LED to light up. But before you get started, there are certainly some more utilities you'll want to learn about.
You have probably noticed that most of the commands you've learned about in this lab are extremely long. Don't worry, you're not expected to memorize these commands. Instead, we just expect you to know what the commands do. To make your life quite a bit easier, we've provided a few scripts you can use. Let's try them out. First, clean out the files in your temporary directory so you can start fresh. Make sure you keep a copy of simple.c, though. Now, follow along:
$ gel-compile simple.c $ ls simple.c simple.o $ gel-link simple.elf simple.o $ ls simple.c simple.elf simple.o $ gel-elf2s19 simple.elf $ ls simple.c simple.elf simple.o simple.s19 $ gel-dump simple.elf simple.elf: file format elf32-m68hc11 Disassembly of section .text: 00008000 <_start>: 8000: 8e ff bf lds #ffbf <_stack> 8003: bf 00 00 sts 0 <_.frame> 8006: 4f clra 8007: 06 tap 8008: bd 80 0d jsr 800d800b: 20 fe bra 800b <_start+0xb> 0000800d : 800d: de 00 ldx *0 <_.frame> 800f: 3c pshx 8010: 9f 00 sts *0 <_.frame> 8012: ce 10 00 ldx #1000 8015: c6 10 ldab #16 8017: e7 00 stab 0,x 8019: 4f clra 801a: 5f clrb 801b: 38 pulx 801c: df 00 stx *0 <_.frame> 801e: 39 rts
As you can see, the gel-compile script compiled simple.c into simple.o, the gel-link script linked simple.o into simple.elf, and the gel-elf2s19 script created simple.s19 from simple.elf. You can also see that running gel-dump on the ELF file produces a disassembly of the code in the ELF file. Perhaps this doesn't seem very useful to you right now, but it will definitely be useful later on.
These scripts should make it significantly easier for you to write HC11 programs. For a brief summary of how to use each script, run it with no arguments (for example, just run gel-link for usage information). You are encouraged to view the contents of these scripts to learn more about how they work. They're located in /opt/m68hc1x-gcc.
Your assignment is to familiarize yourself with the commands discussed in this lab and to modify simple.c to turn on your robot's LED.
While this lab is very long, we hope that you found it very useful and informative.