Programming the HC08 in C language Premek Fiala - Beta Control Jiri Gutman - Motorola
Praha 9. July 2002
Based on lecture by
[email protected] Program Program: 9,00
Prezence účastníků a zahájení
9,30 – 10,30
Kontroléry HC08 ( zobecňující pohled z hlediska vyššího programovacího jazyka)
10,30 – 11,30
jazyk C (zběžně, pouze nejnutnější vlastnosti)
11,30 – 13,00
programování v C pro HC08 (včetně stylu programování pro řídicí aplikace)
13,00 – 14,00
Přestávka na oběd
14,00 – 16,00
C překladač uživatelsky (uživatelské prostředí, struktura překladu), příklady a diskuse
16,00
Ukončení semináře (zájemci obdrží překladač C pro HC08 zdarma) Based on lecture by
[email protected] Co dostanete CodeWarrior Special Edition - HC08 Sada Special Edition pro HC08 je nyní zdarma ke stažení z internetových stránek společnosti Motorola nebo ji lze objednat tamtéž jako CDROM s označením CDCWSEHC08/D. Metrowerks současně nabízí možnost rozšíření sady na zcela neomezenou verzi za poplatek 495 USD, což znamená úsporu přes 2000 USD. Tato nabídka je časově omezena do 22.
července 2002. Více
informací naleznete na http://www.motorola.com/mcu
Based on lecture by
[email protected] 8-Bit MCU Roadmap
Based on lecture by
[email protected] Na čem budeme předvádět The HC08 Low Cost Kits - targets 908GP32, 908JK3 and 908KX8 in 2001
Low Cost kit for HC08 beginners ( void main(void){ printf (“Hello World!\n”); while(1); }
• Startup code • Standard libraries
- Startup code is an extra piece of software that executes prior to main(). - A standard set of function libraries. These include various routines like printf, memcpy, strcmp, scanf and so on.
Based on lecture by
[email protected] Startup Routine The startup code is generally written in assembly language and linked with any executable that you build. It prepares the way for the execution of programs written in a high-level language. 1.
Disable interrupts
2.
Copy any initialized data from ROM to RAM
3.
Zero the uninitialized data area
4.
Allocate space for and initialize the stack
5.
Create and initialize the heap (if available)
6.
Enable interrupts
7.
Call main() (never returns) Based on lecture by
[email protected] Standard Library
Performing Input/Output using the C library functions getchar gets printf putchar puts scanf sprintf sscanf.
#include <stdio.h> void main(void){ printf (“Hello World!\n”); while (1); }
All input/output performed by C library functions is supported by underlying calls to getchar and putchar. The C source code for these and all other C library functions are commonly included
Based on lecture by
[email protected] C for Embedded Systems
Based on lecture by
[email protected] Some natural Questions... After seeing the previous code, some natural questions may arise:
Where is my code? Where are my variables? How do I access I/O Registers? How do I handle interrupts?
Based on lecture by
[email protected] Where is my code?
In the PRM file, you can define where you want to allocate each segments you have defined in your source code. In order to place a segment into a specific memory area; just add the segment name in the PLACEMENT block of your PRM file.
Based on lecture by
[email protected] The Linker Parameter File (PRM)
Based on lecture by
[email protected] Where are my variables?
The variables are placed into the Default_RAM PLACEMENT unless otherwise stated with a #pragma... It is important to define a segment into the Zero Page RAM (0x40 -0xFF) to place the most frequently used variables. This way, the compiler will optimize the code using direct addressing mode (8-bit address) instead of extended mode (16-bit address).
Based on lecture by
[email protected] How to access I/O Registers?
Many I/O and control registers are located in the direct page and they should be declared as such, so the compiler can use the direct addressing mode where possible. One of the first questions that arises when programming embedded is:
How do I access I/O Registers?
The answer is as simple or complicated as you want...
Based on lecture by
[email protected] Defining I/O Registers
One common and very useful form is: #define PortA ( * ( volatile unsigned char * ) 0x0000 )
An easier way to do this is: volatile unsigned char PortA
@0x0000;
A portable way is like this: #define BYTE_REG *(volatile unsigned char*) #define PortA BYTE_REG
0x0000
#define PortB BYTE_REG
0x0001
Based on lecture by
[email protected] This makes PortA a variable of type char at address 0x0000
This is a compilerspecific syntax... It is more readable, but we pay the price loosing portability.
This is portable syntax... For better readability use typedef.
Defining I/O Registers (2) Most convenient way: /*****SCI CONTROL REGISTER 2*****/ volatile union { struct { unsigned char _SBK:1; unsigned char _RWU:1; unsigned char _RE:1; ............. } SCC2_BITS; unsigned char SCC2_BYTE; } SCC2_ @ 0x14; /*DEFINE REGISTER*/ #define SCC2 SCC2_.SCC2_BYTE /*DEFINE REGISTER BITS*/ #define SBK SCC2_.SCC2_BITS._SBK #define RWU SCC2_.SCC2_BITS._RWU #define RE SCC2_.SCC2_BITS._RE ................ Based on lecture by
[email protected] This is a union placed over a byte at 0x14. Register bits can be acessed as an 8-bit entity (SCC2 = 0x04), or as individual bits (RE = 1).
How do I handle interrupts? The CodeWarrior compiler provides a non-ANSI compliant way to specify directly the interrupt vector number in the source: interrupt 17 void TBM_ISR (void){ /* Timebase Module Handler*/ } Vector Number
Vector Address
Vector Address Size
0
0xFFFE - 0xFFFF
2
1
0xFFFC – 0xFFFD
2
2
0xFFFA – 0xFFFB
2
...
...
...
n
0xFFFF – ( n*2 )
2
Based on lecture by
[email protected] Introduction to CodeWarrior
Let’s see how to...
• Create empty project from stationary • Add and remove files from a project • Startup Code • Default.prm Settings • Use of file editor • Compiling (successful and unsuccessful) • Enable debugging and debugging a project • Setting a breakpoint • Viewing variables in debugger Example #1
Based on lecture by
[email protected] Useful ANSI-C Standards
Based on lecture by
[email protected] Data types Facts
• The greatest savings in code size and execution time can be made by choosing the most appropriate data type for variables. • The natural internal data size for 8-bit MCU is 8-bits (one byte), whereas the C preferred data type is ‘int’. • 8-bit machines can process 8-bit data types more efficiently than 16-bit types. • “int” and larger data types should only be used where required by the size of data to be represented. • Double precision and floating point operations are particularly inefficient and should be avoided wherever efficiency is important.
Based on lecture by
[email protected] Scalar Types for the HC08 The ANSI standard does not precisely define the size of its native types, but CodeWarrior does...
0
255
All scalar types (except char) are signed by default Example: ‘int’ = ‘signed int’
Based on lecture by
[email protected] Default CodeWarrior Data Types All basic types can be changed...
Based on lecture by
[email protected] Data Type Selection
There are 3 Rules for Data Type Selection on 8-bit MCUs:
• Use the smallest possible type to get the job done. • Use unsigned type if posibble. • Use casts within expressions to reduce data types to the minimum required.
Based on lecture by
[email protected] Modifiers
Three key words, together, allow us to write not only better code, but also tighter code:
static volatile const
Based on lecture by
[email protected] Static Variables
When applied to variables, static has two primary fuctions:
• The variable doesn’t disappear between successive invocations of a function.
• When declared at the module level, it is accessible by all functions in all the module, but by no one else. Note: These variables won’t be stored in the Stack.
Based on lecture by
[email protected] Static variable Example Before entering to MyFunction the first time, myVar = 0
FILE1.c #include
includes functions contained in file FILE2.c
FILE2.c void MyFunction (void){
void main (void){ MyFunction();
static char myVar = 0;
Definition of MyFunction in FILE2.C local variable declared static
included in FILE2.c myVar = myVar + 1;
MyFunction();
included in FILE2.c }
}
The static Variable keeps its value even though myVar is local
Before entering to MyFunction for the second time: myVar = 1
Based on lecture by
[email protected] Static Functions Features: • Only callable by other functions within its module. • Good structured programming practice. • Can result in smaller and/or faster code. Advantages: Since the compiler knows at compile time exactly what functions can call a given static function, it may strategically place the static function such that may be called using a short version of the call or jump instruction.
Based on lecture by
[email protected] Volatile Variables
A volatile variable is one whose value may be changed outside the normal program flow.
In embedded systems, there are two ways this can happen:
• Via an interrupt service routine • As a consequence of hardware action
Based on lecture by
[email protected] It is very good practice to declare volatile all Peripheral Registers in embedded devices.
Volatile variables are never Optimized
Access to variables defined as volatile are never optimized by the compiler !!! Without Volatile keyword volatile unsigned char PORTA @0x00; volatile unsigned char SCS1 @0x16; unsigned char value;
MOV LDA STA
void main(void){
With Volatile keyword
PORTA = 0x05; PORTA = 0x05; SCS1; value = 10;
/* PORTA = 00000101 */ /* PORTA = 00000101 */
}
Based on lecture by
[email protected] MOV MOV LDA LDX STX
#5,PORTA #10 @value
#5,PORTA #5,PORTA SCS1 #10 @value
Volatile variable Example
/* MC68HC908GP20/32
Official Peripheral Register Names */
volatile volatile volatile volatile volatile
unsigned unsigned unsigned unsigned unsigned
char char char char char
PORTA PORTB PORTC PORTD PORTE
@0x0000; /* Ports and data direction */ @0x0001; @0x0002; @0x0003; @0x0008;
volatile volatile volatile volatile volatile
unsigned unsigned unsigned unsigned unsigned
char char char char char
DDRA DDRB DDRC DDRD DDRE
@0x0004; /* Data Direction Registers */ @0x0005; @0x0006; @0x0007; @0x000C;
volatile unsigned char PTAPUE volatile unsigned char PTCPUE volatile unsigned char PTDPUE
@0x000D; /* Port pull-up enables */ @0x000E; @0x000F;
Based on lecture by
[email protected] Const variables
The Declaration const is applied to any variable, and it tells the compiler to store it in ROM code. The compiler keeps the program memory address of that location. Since it is in ROM, its value cannot be changed. An initialization value must be declared. Example: const double PI = 3.14159265;
Based on lecture by
[email protected] Const constrains
• Some compilers create a genuine variable in RAM to hold the const variable. On RAM-limited systems, this can be a significant penalty. • Some compilers, like CodeWarrior, store the variable in ROM. However, the “read only” variable is still treated as a variable and accessed as such, typically using some form of indexed addressing (16-bit). Compared to immediate addressing (8-bit), this method is normally much slower.
Based on lecture by
[email protected] Const volatile variables
Can a variable be both, const and volatile? Yes! This modifier form should be use on any memory location that can change unexpectedly (volatile) and that is read-only (const). The most obvious example of this is a hardware status register: /* SCI Status Register */ const volatile unsigned char SCS1
Based on lecture by
[email protected] @0x0016
Time Reference Program
Based on lecture by
[email protected] Time Reference Program
Let’s make a program to output a 1 Hz square signal through Port C of the 68HC908GP32 Assume: XTAL = 4.9152MHz
Example #2
Based on lecture by
[email protected] The TBM on the MC68HC908GP32 Time Base Module (TBM) The TBM will generate periodic interrupts at user selectable rates using a counter clocked by the external crystal clock. The TBM uses 15 divider stages, eight of which are user selectable. • User selectable oscillator clock source enable during stop mode to allow periodic wakeup from stop • The Time Base Module Only has One Register for Status and Control
Based on lecture by
[email protected] TBM Programming Sample Turn On the TBM TBON
÷2
÷2
÷2
÷2
÷ 128
÷ 64
Select Timebase Rate ( 8,192 for this example )
÷2
Based on lecture by
[email protected] 0
TBR0
÷2
÷2
TBR1
÷2
÷ 2048
÷2
÷2
TBR2
÷2
÷ 32768
÷2
÷ 32
÷2
÷ 8192
÷2
÷8
÷2
÷ 16
CGMXCLK 32.768kHz
0 0 0 0 1 1 1 1
0 0 1 1 0 0 1 1
0 1 0 1 0 1 0 1
0
1
TBIF 250ms
Signal the event
Time Base Control Register (TBCR)
TACK— Timebase ACKnowledge TBON — Timebase Enabled TBIE — Timebase Interrupt Enabled TBIF — Timebase Interrupt Flag The TACK bit is a write-only bitTimebase and always reads as 0.offWriting a power —enables Timebase Rate This read/write bitTBR2:TBR0 enables the timebase. maySelection be turned to reduce This read/write bit the timebase This read-only flag bit is set when theinterrupt when the consumption its function is not The the counter be initialized by logic 1when to this bit clears TBIF, the timebase interrupt flag bit. Writing TBIF bittimebase becomes set. Reset clears bit. Test bitnecessary. should always 0 can counter has rolled over.beTBIE clearingaand then setting thishas bit. no Reset clears TBON bit. logic 0 to this bit effect. These read/write bits are the used to select the1rate ofinterrupt timebase interrupts. 1 = Timebase enabled = Timebase interrupt pending 1 = Timebase enabled 1 = Clear interrupt flag 0 = Timebase interrupt not pending = timebase Timebase disabled 0 = Timebase0disabled and theinterrupt counter initialized 0 = No effect Based on lecture by
[email protected] See Rate Selection Table
TBM Rate Selection
Time Base Control Register
Based on lecture by
[email protected] Building the TBM C module Defining the TBM Register Object: Definitions in “iogp20_32.h”
typedef unsigned char BYTE; volatile BYTE TBCR
#define TBCR
@0x1C;
Based on lecture by
[email protected] *((volatile BYTE *)0x1C)
Main Program #include "hidef.h" #include "iogp20_32.h" #define unsigned char
COUNT 75 cCounter=0x00;
void main(void) { CONFIG1 = 0x0B; CONFIG2 = 0x03;
/* Interrupts per sec */ /* Interrupt Events Counter */
/* COP dis, STOP En, LVI En @5v */ /* SCI using Int Clk, Osc En @STOP
*/
DDRC = 0xFF; PORTC = 0xFF;
/* Configure PortC as Output */ /* Initialize PortC */
TBCR
/* Configure TimeBase Status & Control Reg TBM OFF & interrupt enabled Select Timebase Rate (Div 2^15) @XTAL=4.9152MHz -> Freq=150Hz */
= 0x04;
cCounter = COUNT; EnableInterrupts;
/* Initialize the Counter */
TBCR |= TBON while(1);
/* Turn TBM on */
}/* END main() */ Based on lecture by
[email protected] Creating a Interrupt Service Routine MC68HC908GP20 Interrupt Vector Table
An ISR in CodeWarrior Time Base Module Vector Location in memory interrupt 17 void TBM_ISR (void){ TBCR |= TACK; // Acknowledge Int ... } B051 B052 B054 B055
PSHH BSET PULH RTI
3,0x1C
Note: The TBM has the lowest vector priority of all interrupts
Vector 17
Address $FFDC
Vector Time Base Module Vector
16
$FFDE
ADC Conversion Complete
15
$FFE0
Keyboard Vector
14
$FFE2
SCI Transmit Vector
13
$FFE4
SCI Receive Vector
12
$FFE6
SCI Error Vector
11
$FFE8
SPI Transmite Vector
10
$FFEA
SPI Receive Vector
9
$FFEC
Timer 2 Overflow Vector
8
$FFEE
Timer 2, Channel 1 Vector
7
$FFF0
Timer 2, Channel 0 Vector
6
$FFF2
Timer 1 Overflow Vector
5
$FFF4
Timer 1, Channel 1 Vector
4
$FFF6
Timer 1 Channel 0 Vector
3
$FFF8
PLL Vector
2
$FFFA
IRQ Vector
1
$FFFC
Software Interrupt Vector
-
$FFFE
Reset
Based on lecture by
[email protected] TBM Interrupt Subroutine
The TBM Interrupts subroutine looks like this...
interrupt 17 void TBM_ISR (void){ TBCR |= TACK;
/* TimeBase Interrupt Acknowledge */
if( !(--cCounter) ){ PORTC = ~PORTC; cCounter = COUNT; }
/* If Counter is ZERO then */ /* Toggle LED */ /* Reinitialize the Counter */
}/* END TBM_ISR() */
Based on lecture by
[email protected] ptejte se, odpovíme
Based on lecture by
[email protected]