Using Enhanced Holtek C Revision: 1.30 Date: March 15, 2011 Using Enhanced Holtek C Table of Contents Chapter 1 Introduction.................................................................................... 6 Chapter 2 Holtek C Compiler User Environment Configuration ................. 7 2.1 Entering the HT-IDE3000, Establishing a new Project, Selecting the Enhanced Holtek C Compiler .......7 2.2 Selecting the Enhanced Holtek C Compiler after program establishment .................................................7 Chapter 3 A Quick Start to Microcontroller C Language Program ............. 9 3.1 Defining the Main Function..............................................................................................................................9 3.2 Defining Sub-functions ....................................................................................................................................9 3.3 Defining Global Variables ..............................................................................................................................10 3.4 Defining the Interrupt Service Routine : ISR ................................................................................................10 3.5 Others ..............................................................................................................................................................11 Chapter 4 The C Program Language ........................................................... 12 4.1 C Language Structure ....................................................................................................................................12 4.2 Start a Program with C Language .................................................................................................................12 4.2.1 Define main ............................................................................................................................................12 4.2.2 Include a Header File .............................................................................................................................13 4.2.3 Define symbols and variables ................................................................................................................13 4.2.4 Setup the initial status of the MCU.........................................................................................................14 4.2.5 Define the sub-functions ........................................................................................................................14 4.2.6 Design the Interrupt Service Routine .....................................................................................................15 4.3 Variable and Data Type...................................................................................................................................16 4.3.1 Variable Name .......................................................................................................................................16 4.3.2 Data Type...............................................................................................................................................16 4.3.3 Variable Effective Scope ........................................................................................................................16 4.3.4 Data Type...............................................................................................................................................17 4.3.5 Bit Data Type .........................................................................................................................................17 4.3.6 Storage Class and Qualifier ...................................................................................................................18 4.3.7 Absolute Variable ...................................................................................................................................19 4.3.8 Constant.................................................................................................................................................19 4.3.9 Pointer and Array ...................................................................................................................................20 4.3.10 struct and union ...................................................................................................................................21 4.4 Operators ........................................................................................................................................................22 4.5 Program flow control .....................................................................................................................................25 4.5.1 if-else statement.....................................................................................................................................25 4.5.2 Switch statements ..................................................................................................................................25 4.5.3 for statement ..........................................................................................................................................26 4.5.4 while statement ......................................................................................................................................27 4.5.5 do-while statement .................................................................................................................................27 4.5.6 goto statement .......................................................................................................................................27 4.5.7 break and continue statement................................................................................................................27 4.6 Functions ........................................................................................................................................................28 4.6.1 Arguments ..............................................................................................................................................28 4.6.2 Return Values ........................................................................................................................................28 4.7 Interrupt Service Routines .............................................................................................................................29 4.8 Inline Assembly Codes in C...........................................................................................................................30 4.9 Preprocessor ..................................................................................................................................................32 4.9.1 Define Characters (#define) ...................................................................................................................32 4.9.2 #include..................................................................................................................................................32 4.9.3 Inline Assembly ......................................................................................................................................32 Rev. 1.30 2 March 15, 2011 Using Enhanced Holtek C 4.9.4 Conditional Compiling (#if/#endif) ..........................................................................................................33 4.9.5 Compiler Special Options pragma .........................................................................................................34 4.10 Holtek C built-in Functions ..........................................................................................................................36 Chapter 5 Basic C Language Programs...................................................... 37 5.1 C Syntax Concepts .........................................................................................................................................37 5.2 Loop Application ............................................................................................................................................38 5.2.1 Example 1: 8 Flashing LEDs..................................................................................................................38 5.2.2 Delay Timing ..........................................................................................................................................38 5.3 Notes for writing an MCU Application Program ..........................................................................................40 5.4 Microcontroller Application Program Example ..........................................................................................41 5.5 Points to Note for Microcontroller Application Program Design ...............................................................42 Chapter 6 Example Program – Basic Level ................................................ 44 6.1 LED Running Light Display ...........................................................................................................................44 6.1.1 Objective ................................................................................................................................................44 6.1.2 Peripheral Components .........................................................................................................................44 6.1.3 Circuit Diagram ......................................................................................................................................44 6.1.4 MCU Configuration Options ..................................................................................................................44 6.1.5 Program Flow.........................................................................................................................................45 6.1.6 Original Program ....................................................................................................................................45 6.1.7 Program Description ..............................................................................................................................45 6.2 LED Blinking Light..........................................................................................................................................46 6.2.1 Objective ................................................................................................................................................46 6.2.2 Peripheral Components .........................................................................................................................46 6.2.3 Circuit Diagram ......................................................................................................................................46 6.2.4 MCU Configuration Options ...................................................................................................................46 6.2.5 Program Flow.........................................................................................................................................47 6.2.6 Original Program ....................................................................................................................................47 6.2.7 Program Description ..............................................................................................................................47 6.3 Single 7-segment Display ..............................................................................................................................48 6.3.1 Objective ................................................................................................................................................48 6.3.2 Peripheral Components .........................................................................................................................48 6.3.3 Circuit Diagram ......................................................................................................................................48 6.3.4 Microcontroller Configuration Options....................................................................................................48 6.3.5 Program Flow.........................................................................................................................................49 6.3.6 Original Program ....................................................................................................................................49 6.3.7 Program Description ..............................................................................................................................49 6.4 5*5 Dot Matrix LED Display ............................................................................................................................49 6.4.1 Objective ................................................................................................................................................49 6.4.2 Peripheral Components .........................................................................................................................49 6.4.3 Circuit Diagram ......................................................................................................................................50 6.4.4 Microcontroller Configuration Options....................................................................................................50 6.4.5 Program Flow.........................................................................................................................................50 6.4.6 Original Program ....................................................................................................................................51 6.4.7 Program Description ..............................................................................................................................51 6.5 HT1621 LCD Control using the HT48 Series of MCUs ................................................................................52 6.5.1 Objective ................................................................................................................................................52 6.5.2 Peripheral Components .........................................................................................................................52 6.5.3 Circuit .....................................................................................................................................................55 6.5.4 Configuration Option ..............................................................................................................................55 6.5.5 Program Flow.........................................................................................................................................55 6.5.6 Original Program ....................................................................................................................................56 6.5.7 Program Description ..............................................................................................................................59 6.6 Using the HT48 to control the LCD panel .....................................................................................................61 Rev. 1.30 3 March 15, 2011 Using Enhanced Holtek C 6.6.1 Objective ................................................................................................................................................61 6.6.2 Peripheral Components .........................................................................................................................61 6.6.3 Circuit Diagram ......................................................................................................................................62 6.6.4 Configuration Option ..............................................................................................................................62 6.6.5 Program Flow.........................................................................................................................................62 6.6.6 Original Program ....................................................................................................................................63 6.6.7 Program Description ..............................................................................................................................64 6.7 HT46R63 MCU with LCD Driver Application Program .................................................................................66 6.7.1 Objective ................................................................................................................................................66 6.7.2 Peripheral Components .........................................................................................................................66 6.7.3 Circuit Diagram ......................................................................................................................................67 6.7.4 MCU Configuration Option .....................................................................................................................67 6.7.5 Program Flow.........................................................................................................................................67 6.7.6 Original Program ....................................................................................................................................68 6.7.7 Program Description ..............................................................................................................................69 6.8 Display General Functions – HD44780 LCM ................................................................................................69 6.8.1 initialLCD( ) ............................................................................................................................................69 6.8.2 PutChar( ) ..............................................................................................................................................70 6.8.3 MoveAt( ) ...............................................................................................................................................70 6.8.4 main( ) ....................................................................................................................................................70 6.9 Key Scan Program ..........................................................................................................................................71 6.9.1 Objective ................................................................................................................................................71 6.9.2 Peripheral Components .........................................................................................................................71 6.9.3 Circuit Diagram ......................................................................................................................................71 6.9.4 MCU Configuration Options ...................................................................................................................72 6.9.5 Program Flow.........................................................................................................................................72 6.9.6 Original Program ....................................................................................................................................73 6.9.7 Program Description ..............................................................................................................................73 Chapter 7 Application Example – Interrupt Program ................................. 75 7.1 LED Control using the Timer .........................................................................................................................75 7.1.1 Objective ................................................................................................................................................75 7.1.2 Peripheral Components .........................................................................................................................76 7.1.3 Circuit Diagram ......................................................................................................................................77 7.1.4 MCU Configuration Option .....................................................................................................................77 7.1.5 Program Flow.........................................................................................................................................77 7.1.6 Original Program ....................................................................................................................................78 7.1.7 Program Description ..............................................................................................................................78 7.2 Analog to Digital Converter Application .......................................................................................................79 7.2.1 Objective ................................................................................................................................................79 7.2.2 Peripheral Components .........................................................................................................................79 7.2.3 Circuit Diagram ......................................................................................................................................81 7.2.4 MCU Configuration Options ..................................................................................................................82 7.2.5 Program Flow.........................................................................................................................................82 7.2.6 Original Program ....................................................................................................................................82 7.2.7 Program Description ..............................................................................................................................84 Chapter 8 NiMH Battery Charger (HA0084T) Application using the HT46R52A85 8.1 Objective .........................................................................................................................................................85 8.2 Register and Peripheral Components ..........................................................................................................86 8.3 Circuit Diagram ...............................................................................................................................................88 8.4 MCU Configuration Options ..........................................................................................................................88 8.5 Program Flow ..................................................................................................................................................89 8.6 Original Program and Program Description ................................................................................................93 Rev. 1.30 4 March 15, 2011 Using Enhanced Holtek C Chapter 9 Program Example – The HT46R74D-1 Tyre Pressure Gauge 109 9.1 Objective .......................................................................................................................................................109 9.2 Functions ......................................................................................................................................................109 9.3 Circuit Diagram .............................................................................................................................................110 9.4 MCU Configuration Options ........................................................................................................................114 9.5 Program Flowchart .......................................................................................................................................115 9.6 Original Program and Program Description ..............................................................................................120 Chapter 10 Mixed Language –Assembly and C ....................................... 136 10.1 Naming of Variables, Functions and Parameters ....................................................................................136 10.2 Parameter Passing .....................................................................................................................................136 10.3 Return Value Setting ..................................................................................................................................137 10.4 Calling Assembly Functions from the C Program ...................................................................................137 10.5 Calling C Function from Assembly Program ...........................................................................................138 Rev. 1.30 5 March 15, 2011 Using Enhanced Holtek C Chapter 1 Introduction Applications for Holtek Semiconductor's range of 8-bit microcontrollers, can be developed using both assembly language and the Holtek Enhanced C language. For this latter case Holtek has provided a C compiler for user convenience. As the program and data memory space of some 8-bit microcontrollers can be somewhat limited in capacity, assembly language is often chosen for application development due its lower demands on memory resources. However, as microcontroller development trends see the release of devices with higher memory capacities, continuing to use assembly language brings with it the complications of longer application program development time and more complex future program maintenance and upgrade.. Using the C language, can reduce these difficulties due to its higher readability and portability features. Also in addition to reduced program implementation times, it is also easier for users to switch to other microcontrollers and more easier to reduce or increase functions. These features make C an extremely efficient program choice for MCU application program development. This book describes how to use the Holtek Enhanced C language to write application programs for Holtek microcontrollers , and covers items such as program structure, general usage, special usage as well as including a range of application examples. The book also shows any special considerations that need to be taken into account during application program development . Readers can use, modify and apply the supplied application programs for their own application development verifying the results using the Holtek HT-ICE and HTIDE3000 development tools Chapter 2 gives a brief introduction on the configuration steps of the Holtek C compiler, showing how to setup the HT-IDE3000 for Holtek C compiler use. Chapter 3 shows a quick method to get users writing C programs as quickly as possible. For users who are already familiar with ANSI C language, it is possible to start writing a C program very quickly after reading this chapter. Chapter 4 introduces the C language in more detail and those who are not familiar with C should read this chapter carefully to get going with using the C language. Chapter 5 introduces more about basic concepts, special notes and recommended methods for writing programs using C. Finally Chapters 6 to 9 provide C language program application examples using actual Holtek microcontrollers. Rev. 1.30 6 March 15, 2011 Using Enhanced Holtek C Chapter 2 Holtek C Compiler User Environment Configuration 2.1 Entering the HT-IDE3000, Establishing a new Project, Selecting the Enhanced Holtek C Compiler After entering the HT-IDE3000 development environment, adhere to the following steps to establish a new project: → Select Project from the menu → Select New from within the Project menu → The window shown below should now appear. In the Language Tool area select Enhanced Holtek C Compiler/Assembler. 2.2 Selecting the Enhanced Holtek C Compiler after program establishment After the project is open, select Project Setting from within the Option menu, and in the Language Tool area select the Enhanced Holtek C Compiler/Assembler. Rev. 1.30 7 March 15, 2011 Using Enhanced Holtek C The Enhanced C compiler contains the ehcc32srsc.exe, ehcc32mrsc.exe and ehcc32mrmc. exe files. Rev. 1.30 8 March 15, 2011 Using Enhanced Holtek C Chapter 3 A Quick Start to Microcontroller C Language Program The following chapter introduces how to quickly write a microcontroller application program in C language. Those who are familiar with ANSI C standard language or have some programming experience may start immediately writing a C microcontroller application program. The following chapters are provided as the fundamentals of a basic C program, in which some elements are essential such as 3.1, while other elements may only be required according to specific application needs. 3.1 Defining the Main Function #include “ht46r63.h" void main(void) { int Flag ; …… TurnOn_LCD() ; Flag = LCD_display(cstr) ; TurnOff_LCD() ; …… } The file ht46r63.h defines the constants relevant to the microcontroller, such as the register addresses. This enhances greatly the readability of the program 3.2 Defining Sub-functions To include sub-functions or not depends on the size and functions of the program. Basically the method is just to form a series of smaller modules for the application program instead of putting the whole program into the main function. For rapid implementation and understanding of the application program, the main function only needs to contain call definition functions. For example, a function to enable or disable the LCD can be defined individually as a single sub-function, such as in the example below, which can then be called by any other function. If designed as a general type, it can also be included into the program database via the Library Manager for other projects to use. void TurnOn_LCD(void) { } int LCD_display(char *cstr) { } void TurnOff_LCD(void) { } The parameters and return values of the sub-function can be of basic types such as int, float, char, void, or structure types as struct, union, enum, array, pointer types etc. To avoid the function calling itself, the maximum number of function call levels should be checked against the number of MCU stack levels. Rev. 1.30 9 March 15, 2011 Using Enhanced Holtek C 3.3 Defining Global Variables Some variables will be required for data storage during program execution. Owing to the restrictions of microcontroller memory size and the C compiler, it is recommended to define these general variables as global variables which will be more effective both for program compiling and execution. For example, defining the constant type of pointer variable cstr as the alphabetic string “Hello!" can be done as follows: char * const cstr = “Hello!"; To initialize the global variables in Holtek C, all should be defined as const or constant int type. Ex. const char aa = 3 constant int bb = 3 3.4 Defining the Interrupt Service Routine : ISR If the MCU internal functions contain interrupt functions, the interrupt service routine, ISR, of the internal function should be defined in the following way: #pragma vector ISR_tmr0 @ 0x0c void ISR_tmr0(void) { tick++ ; } The interrupt service routine must obey the following rules: → The returned data type must be void → No parameters included - must be void → Must use the previous process instruction #pragma vector to setup the interrupt vector by adding @ after the function name (ISR_tmr0 in this example) and using the previously defined interrupt vector constant For example: #define VECTOR_TMR0 0x0c #pragma vector ISR_tmr0 @ VECTOR_TMR0 void ISR_tmr0(void) { } → There are two methods for calling a general function within an interrupt 1、Direct calling: the called function must be declared to be nolocal to remove the risk of using the same RAM area. For example: #pragma vector ISR_tmr0 @ 0x0c #pragma nolocal fun fun() { } void ISR_tmr0(void) { fun() ; } 2、Embedded assembly language: for example: #pragma vector ISR_tmr0 @ 0x0c fun() { } void ISR_tmr0(void) { Rev. 1.30 10 March 15, 2011 Using Enhanced Holtek C #asm call _fun; #endasm } Note: In general, calling a general function from within an interrupt is not recommended. 3.5 Others The main function, sub-functions and interrupt service routines mentioned above need not be defined in the same original program file. To be more efficient, it is better to provide the definitions separately in different files with a meaningful file name making it easier to locate the functions that are required in the future. Rev. 1.30 11 March 15, 2011 Using Enhanced Holtek C Chapter 4 The C Program Language Essentially Holtek C is a simulated C language from ANSI (American National Standards Institute.) In order to accomodate the structure of Holtek's 8-bit microcontrollers, some specific syntax for storing and controlling the MCU data are provided. In addition, this chapter also introduces a way to design and write an MCU application program using C language for 8-bit microcontrollers. 4.1 C Language Structure A C language program is composed of statements which must be followed with a semicolon “;" at the end of each statement as an ending symbol. The statements are classified into four kinds: → Declaration: Declare the variable and data type, and the data structure Ex. char flag, tick_cnt ; // declare variable flag and tick_cnt as char type → Definition: Define the variable value and address Ex. int total = 10 ; // define variable total with a setting value of 10 → Expression: Execute mathematics and logic calculation, control the program flow Ex. count = (input>10) ? 10 : input ; → Function Call: Execute a function Ex. putchar(ch) ; // write a character to the output port Each statement can have an added note for description. The C compiler will not compile the notes. The following two types of notes are acceptable: → Digits and characters within symbol /* and /*, including the line feed character`\n' If /* and /* are not in the same line, then all the content in between them will be regarded as notes. Ex. /* this is a comment 1 */ → Content from the symbol // to the end of the line will be regarded as notes. Ex. // This is the another way of writing notes. In the C language, the program block is defined by the function format, so all the statements to be executed need to be defined (included) into some function, such as a description formula. 4.2 Start a Program with C Language Follow the steps below to write a simple application program using C language. 4.2.1 Define main Ex. void main(void) { } In the C language, main is the start of a program execution, similar to a start in assembly language ORG 00 jmp start start: void is the data type. main and void are both reserved words that must be included and must be written in lower case. Rev. 1.30 12 March 15, 2011 Using Enhanced Holtek C 4.2.2 Include a Header File Ex. #include “ht46r63.h" // include a header file ht46r63.h void main(void) { } The header file ht46r63.h provides many definitions of variable and symbols relevant to the MCU for program writing. The advantage is that the program will be easier to understand during writing or for future maintenance by improving program readability. Ex. unsigned char _pa @0x12 ; Define _pa as a unsigned char type of variable with an address at 0x12 of the RAM (port A), so the statement will be as shown below in the program: _pa = 0 ; which has the same function as CLR PA ; (PA=[12H]) in the assembly language. 4.2.3 Define symbols and variables Using symbols makes the program easier to read and modify. For example, define symbol pa0 as shown below: #define _pa0 _12_0 means _pa0 is bit 0 of the RAM address 12H, namely bit 0 of Port A: _pa0 = 1 ; means to set bit 0 of Port A to 1, which is of the same function as SET [12H].0 in assembly language. The previous processing instruction #define is to define a symbol with a specific value, alphabetic string, or macro instruction. The C compiler preprocessor will replace these defined symbols before compiling. The previous processing instruction #undef is to delete the definitions that have been given the symbols, making them invalid. For a more detailed description refer to Chapter 4.9. Variables or symbols in other MCU registers are defined in the header files of the corresponding MCU for programming reference. Some required variables or symbols not defined in the header file can be made for the convenience of program development and maintenance. For example: #include“ht48R50A-1.h" #define scl_pa3 // SCL (clock line) connected to bit 3 of Port A in the MCU #define scl_c_13_3 // bit 3 of Port A in the register (bit type variable) #define scl_pa1 // SDA (data line) connected to bit 1 of Port A in the MCU #define scl_c_13_1 // bit 1 of A port in the register void main(void) { } Define the four symbols, scl, scl_c, sda, sda_c as different output/input ports. Define the variables to take up RAM/ROM space. If the address is designated, this variable will occupy the address, otherwise no address will be assigned to the variable until the Linker is activated. The function is the same as the assembly language below. _pa DB ? The function of symbol definition is the same as EQU in the assembly language. Ex: #define scl_pa3 and scl EQU _pa3 have the same effect Rev. 1.30 13 March 15, 2011 Using Enhanced Holtek C 4.2.4 Setup the initial status of the MCU Setup the initial value of each device in the MCU according to the program function, such as the initial status of peripheral devices and registers etc. scl_c = 0 ; // set SCL (=PAC) status as output sda_c = 0 ; // set SDA (=PA) status as output 4.2.5 Define the sub-functions Individual functions can be implemented using sub-functions which are very useful for program error detection, ease of maintenance and reuse purposes. The function parameters, return values and so forth should be noted during definition. Generally the main() will be put at the end of the program with each sub-function defined at the front or in the other program files. The following example shows a part of it whose detailed description can be referred to in Chapter 4.6. #include “ht48r50a-1.h" // include header file #define scl_pa3 // SCL (clock line) connect to the 3rd bit of A port in the MCU #define scl_c13_3 // the 3rd bit of Port A (address 0x13) in the register (bit type variable) #define sda_pa1 // SDA (data line) connect the 1st bit of Port A in the MCU #define sda_c_13_1 // the 1st bit of the Port A (address 0x13) in the register // function: StartCondition() sub-function // function: start a command // input: NA // output: NA void StartCondition(void) { sda=1 ; //SDA output high scl=1 ; //SCL pull high sda=0 ; // SDA output low scl=0 ; // complete start command } // function: StopCondition() // function: end the previous command // input: NA // output: NA void StopCondition(void) { } // function: main() // function: main function // input: NA // output: NA void main (void) { unsigned char Rdata, type ; // initial setting of the register scl_c = sda_c = 0 ; // set the 1st and 3rd bits of Port A as output types (SCL, SDA as output) StartCondition() ; // call sub-function …… } Rev. 1.30 14 March 15, 2011 Using Enhanced Holtek C 4.2.6 Design the Interrupt Service Routine For microcontrollers with hardware interrupts, it is necessary to design an interrupt service routine to deal with the interrupt events. Define the interrupt service function of the peripheral device (Interrupt Service Routine, ISR) as shown below: #pragma vector ISR_tmr0 @ 0x0c void ISR_tmr0(void) { tick++ ; } The interrupt service routine must obey the following rules: → The return data type must be void → No parameters included → The interrupt vector must be set in the function (here in the example is added a @ and interrupt vector (here 0x0c) after ISR_tmr0) or using the previously defined constants. For example: #define VECTOR_TMR0 0x0c #pragma vector ISR_tmr0 @ VECTOR_TMR0 void ISR_tmr0(void) { } Rev. 1.30 15 March 15, 2011 Using Enhanced Holtek C 4.3 Variable and Data Type During programming, some data such as flags, execution times, delay time etc. may need to be stored for which the variable definitions must be setup in advance. Due to the necessity of using ROM or RAM, before using the variables, the data type must be defined for the compiler to correctly compile the program and configure the memory space. Additionally the data type, storage class and qualifiers can be added. 4.3.1 Variable Name Variable Naming Rules → The first character must be an English letter or underscored symbol which can be followed by letters or digits → The previous 32 characters of the variable name is effective → Symbols like +, -, *, /…etc. cannot not be included in the variable → Case-sensitive, ex. count and Count belong to different variable names Example: number, total_tick , _tick are legal variable names while 2num, $dot, line\n are illegal. 4.3.2 Data Type Type bit char signed char unsigned char short unsigned short int unsigned int long unsigned long void float double Size (bits) 1 8 8 8 16 16 16 16 32 32 0 32 32 Arithmetic Type unsigned integer signed integer signed integer unsigned integer signed integer unsigned integer signed integer unsigned integer signed integer unsigned integer — real real Scope 0, 1 -128 ~ +127 -128 ~ +127 0 ~ 255 -32768 ~ +32767 0 ~ 65535 -32768 ~ +32767 0 ~ 65535 -2147483648 ~ +2147483647 0 ~ 4294967295 — -3.4028e+38~3.4028e+38* -3.4028e+38~3.4028e+38* * float, double uses the IEEE754 32bit format. 4.3.3 Variable Effective Scope Determines the effective scope of the variable according to its definition, such as: → Local Variable Those being defined in the program block (ex. the function) are all local variables which are only effective when the program block is executed and invalid after completion. The program block indicates the statements within “{“ and "}". The static variables defined in the function are all global variables. Refer to the 4.3.2 description. → Global Variable Those being defined outside the program block are global variables which are all effective during program execution and can be accessed or modified by any function. Ex: #include “ht48r50a-1.h" unsigned char flag ; // global variable void main(void) { char type ; // local variable, effective only when the function is executed static status = 0 ;// static variable, set to 0 only at first execution …… } Rev. 1.30 16 March 15, 2011 Using Enhanced Holtek C 4.3.4 Data Type When making variable declarations, the data type must be designated so as to inform the compiler of the required memory size. The data type can be Integer type and Floating Point type, in which the integer type can be subdivided into signed and unsigned. ■ Integer → char This occupies one byte of memory space with a range of -128 to 127 if “signed" added or 0 to 255 if “unsigned". If no signed or unsigned is marked, it will be regarded as signed which can be used to define characters such as `A', `d', `$', `3'…etc. → short This occupies 2 bytes of memory space with a range of -32768 to 32767 if signed or 0 to 65535 if unsigned. If neither signed nor unsigned, it will be regarded as signed. Holtek C uses little-endian format, saving the least significant byte of the variable at the low address of the memory. Ex. variable count = 0x1234 is saved at 40H address of the memory while the low byte value 0x34 will be saved at 40H and the high byte 0x12 at 41H. → int same as short type → long This occupies 4 bytes of memory space with a scope of -2147483648 to 2147483647 if signed or 0 to 4294967295 if unsigned. If neither signed nor unsigned, it will be regarded as signed in the little-endian format. The 32-bit variable is first saved with the least significant byte of the least significant word to the low address of the memory and then the high byte of the least significant word to the least significant byte of the high word and finally the high byte of the high word. ■ Floating Point Holtek C supports IEEE 754 32 bit format, including float and double data types. The floating point value is stored in memory in the format as shown in the table below: IEEE 754 32 bit sign x biased exponent xxxx xxxx mantissa xxx xxxx xxxx xxxx xxxx xxxx The floating point value type is: number = (-1)sign x 2(exponent – 127) x 1.mantissa For example, a floating point value of 2.77000e+37 will be saved as 7DA6B69B in the memory using 32 bits in total. 32bit Memory value sign 7DA6B69Bh 0 Biased exponent 1111 1011b (=251) 1.mantissa Decimal value 1.0100110101101101001101b 2.77000e+37 (=1.302447676659) 4.3.5 Bit Data Type This data type is similar to the integer type except that there are only two values of 0 and 1, so only the LSB (least significant bit) of the integer will be selected by the following specific usages: → bit type can be neither used as a pointer data type nor defined as a const → bit type can be setup as the return type of a function, which is placed in the relative location of an accumulator. → the initial value of the bit type variable will not be setup during program execution, so the program should set the initial value by itself. → a local bit variable will occupy a byte (with only one bit effective) while a global will occupy a bit. Rev. 1.30 17 March 15, 2011 Using Enhanced Holtek C → the following shows the legal usage: static bit init_flag ; // definition inside the function will be regarded as local variables bit toggle_flag ; → ex. int data = 0x54 ; bit flag ; flag = data ; then flag = 0 (take the data LSB) If the microcontroller contains more than one RAM bank, such as in the HT46R63, the bit type variable definition should point to RAM bank 0 using the preprocessor instruction #pragma rambank0 as below: #pragma rambank0 bit flag ; #pragma norambank 4.3.6 Storage Class and Qualifier Storage Class auto, register, static, extern Qualifier const, constant ,volatile, persistent Specifier typedef The variable data type should be assigned first at its declaration or definition while the storage class and qualifier are optional according to the actual application requirement. ■ Storage class The storage class is related to the local variable and global variable. → Storage class auto auto is used by the local variable that has no storage class assigned. Writing auto or not has the same effectiveness. The local variable is placed in RAM bank 0. → Storage class register register is similar to auto which is also used by the local variable. When variable accesses are frequent, the local variable can be set to register and the C compiler will use the register instead of the non-data memory space to store this variable so as to enhance the access speed and reduce compilation. This function is currently not applied. → Storage class static The static variable will remain effective until the whole program ends. Its initial value will only be set once at the beginning of program execution. Though effective before the program comes to an end, the static variable defined inside the function is still a local variable which must be read/written within the function that defines it. → Storage extern extern is used to inform the C compiler that this variable is defined in another program file that needs to be connected using the Linker so as to know the variable location. For the present microcontroller applications, it is recommended to use extern rather than the other three which have no special advantages and can be functionally replaced by defining a global variable. ■ Qualifier → Qualifier const The C compiler will place the const variable into the program memory. When defining a const, a value which cannot be changed during the program execution must be setup. The const variable cannot be configured as extern at present. → Qualifier Constant This qualifier, exclusively provided by the Holtek C compiler, will place the constant Rev. 1.30 18 March 15, 2011 Using Enhanced Holtek C variable in the last page of the program memory and should be configured when defining the const variable. The value cannot be changed during program execution. Three notes for using this qualifier are as follows: ● Used for int or unsigned int data types only ● The setting value adjusts to the program memory width of the microcontroller. For example, when used in the HT48R50A-1, which has a Program Memory width of 15 bits, the high bit will be invalid, so 0x9A will be changed to 0x1A by the C compiler and the highest bit, which is bit 15, will be cleared to zero. ● All variables or arrays defined with this qualifier cannot exceed a total of 255 words ● To set the int or unsigned int variable to a constant value, it is recommended to use constant instead of const so as to increase the program efficiency. ■ Specifier → Specifier typedef typedef is used to define a new name declaration in light of the data type instead of the new variable of the data type. Ex. declare UCHAR (new name) to be an unsigned char data type by using: typedef unsigned char UCHAR ; // UCHAR is the new name of the unsigned char UCHAR count ; // the data type of the variable count is unsigned char // equal to unsigned char count; Using typedef to declare the new name of the data type makes the program easier to be understood with better readability. Ex. typedef unsigned int WORD ; // WORD represents and unsigned 16-bit integer typedef unsigned long DWORD ; // DWORD represents a 32-bit double word 4.3.7 Absolute Variable This variable can point to global or static variables at a fixed memory address. Ex. unsigned char PortA @ 0x12 ; By adding @ and the address after the variable name, the C compiler will change absolute variables in the program to this address during compilation yet does not reserve any position in the memory for it. Therefore the variable will not be found in the map file generated from the linker and will be interpreted by the C compiler as an EQU assembly language instruction. Ex. _PortA EQU 12h This is basically a definition given to the register of the microcontroller program reading convenience. 4.3.8 Constant The integral constant can be listed in a radix format. Radix Constant format Example Binary system 0bnumber or 0Bnumber 0b10111110 Octal number system 0number 0276 Decimal system number 190 Hexadecimal system 0xnumber or 0Xnumber 0xBE If the last character of the constant is an I or L then this indicates that either a signed long or unsigned long type is used. A suffix of u or U after S indicates the constant is an unsigned type. The floating point constant type is double, or else float if suffixed with an f or F. The character constant must be included in single quotation marks, ex. `a' The string constant must be included in double quotation marks, ex. “Hello!" The definition of the string constant will influence the memory address where it is stored. For example: char *cp = “one" ; // C compiler will display an error Rev. 1.30 19 March 15, 2011 Using Enhanced Holtek C char *const sptr = “Hello" ;// “Hello" is stored in the program memory The constant type variable or array must be configured with a value or the C compiler will display an error message as shown in the above example. 4.3.9 Pointer and Array The pointer itself is a variable that stores the address of other variables such as an indirect address in assembly language. The pointer must point to a defined (stored in memory) variable when being used, or an error may occur during execution. The pointer declaration format is: Data type * pointer name [, * pointer name,….]; The data type means the variable data type indicated by the pointer, such as char, of which the name is similar to that of a variable and can be declared to indicate different pointers of the same data type in the same line. For example, char *tptr, *array_ptr ; int *line_ptr ; The above merely declares the pointer variable. The pointer must point to defined variables to become effective. String arrays are not supported yet. For example, char *rainbow[] = { "red", "orange", "yellow" };// not supported ■ Pointer operator & and * The operator & preceding a variable, such as &line, means to obtain the memory address of the variable. For example, int line ; // define variable int *line_ptr ; // declare pointer line = 12 ; line_ptr = &line ; If the variable line is placed at the address 64 of the RAM, the pointer line_ptr is equal to 64. The operator * preceding a variable means to obtain the variable content indicated by the pointer as shown in the above example. int total ; // define variable total = *line_ptr ; // obtain the line content pointed to by line_ptr, the variable total is equal to 12 Regarding the function pointer, currently the enhanced Holtek C only supports the global const function pointer, in which no parameters can be included. Holtek C does not support function pointers. For example: fun() { return 1; } int (*const p)() =fun; // global and initialization required void main() { int a; a=(*p)(); } The pointer size depends on the microcontroller memory size. If there is only one RAM bank, the pointer itself will take one byte while multiple RAM banks will take two bytes. If the variable contains const or constant qualifiers, the pointer will point to the program memory in which the variable content cannot be modified. If no const or constant is included in the variable, the pointer will point to the variable in the data memory RAM. Holtek C cannot obtain the constant address of const. For example: Rev. 1.30 20 March 15, 2011 Using Enhanced Holtek C const int ldc = 0; void main(){ int *a; a = &ldc; // cannot pass } ■ Array An array is composed of elements of the same data type. Ex. char array_name[32] ; is composed of 32 char type elements whose name is array_name using an index to distinguish each element. As an example, array_name[3] means the fourth element. The array index is a positive integer starting from zero up to the total number of elements minus one. For example, the last element is array_name[31] in the above example. This data type is very useful for table production. An array can also be used as a pointer only in a different format. The following example explains the usage between pointers and arrays. char *nptr, *fptr ; // declare pointers char ch, tbl ; // define variables char table[5] = { `a' , `b' , `c' , `d' , `e' } ; // define arrays nptr = table ; // pointer nptr points to the first element table[0] of the array table ch = *nptr ; // save character `a' to variable ch tbl = table[0] ; // save character`a'to variable tbl, so both ch and tbl variables are the same. fptr = &table[4] ; // pointer fptr points to the 5th element, table[4],of the array table ch = *fptr ; // save the character `e' (the 5th element) to variable ch tbl = *(nptr+4) ; // nptr points to the first element of the array table plus 4, therefore bl =`e' Note: The maximum value of the array size depends on the RAM/ROM size of the MCU, therefore the length of const arrays will not exceed one rombank(2000h) and the length of constant arrays will not exceed one rompage(256h). A general array will not exceed 256 bytes. Arrays of three dimensions or more are not presently supported. 4.3.10 struct and union A structure is a collection of one or more variables, possibly of different data types and managed under the same structure name. A structure declaration is equal to a new defined data type. When a variable is declared as a structure type, the content can be read/written using the structure name. The variable data type cannot be in a bit form but can be declared with a bit-field method, for example: struct str_name { unsigned flag : 1 ; // this variable is placed at the least significant bit unsigned no_used : 7 ; unsigned stack : 5 ; // this variable is placed at the highest bits } usage ; Every bit field will be placed into an 8-bit unit with no crossover into two 8-bit units and cannot defined in a format of more than 9-bit bit field unions with the same structure. The only difference lies in the configuration of the memory space. A union provides a way to manipulate different kinds of data in a single storage area. Each variable must declare its data type while the union size is the largest data type size. The start address of every variable is the same, that is the union address. union union_name { char num_byte ; // occupy one byte int num_int ; // occupy 2 byte long num_long ; // occupy 4 byte Rev. 1.30 21 March 15, 2011 Using Enhanced Holtek C } number ; The union number is 4 bytes. Qualifiers can also be used in the structure and union types, such as const, with the same result as the general data type. ■ Structure Operator -> and . If the variable data type indicated by the pointer is a structure or union, either the operator of a right arrow “->" or a dot “." with the variable name can be used to manipulate the variables. Example: struct tag { char flag ; int number ; unsigned ac : 1 ; unsigned z : 1 ; }; struct tag status ; // define the variable status as a structure tag data type struct tag *sptr ; // declare the pointer sptr as a structure tag data type bit vac, pac ; // define the variable vac, pac as a bit data type status.flag = 1 ; // write 1 to the structure variable flag status.ac = 1 ; // write 1 to the structure variable ac vac = status.ac ; // vac = 1 sptr = &status ; // pointer sptr points to the structure status pac = sptr->ac ; // pac = 1 4.4 Operators Operators are arithmetic symbols for the data stored in the variables. An expression is a group of operators, data, variables, function and expression in which the operators include: → Arithmetic operators +, -, *, /, % are addition, subtraction, multiplication, division and remainder operators. → Comparison operators Compare two operands and report the latter result to be true (not zero) or false (zero) > greater than >= greater than or equal to < smaller than <= smaller than or equal to == equal to != unequal to → Logical operators Take the true or false condition of the operators for logical operation and report if the result is true (not zero) or false (zero) && logic AND | | logic OR ! logic NOT → Bitwise logical operators Bit logic operation & bit AND | bit OR Rev. 1.30 22 March 15, 2011 Using Enhanced Holtek C ^ bit XOR ~ bit complement >> bit move to right << bit move to left → Assignment operators <var> += <expr>; add expr value to the variable, save the result to the variable <var> -=<expr>; subtract expr value to the variable, save the result to the variable <var> *=<expr>; multiply expr value to the variable, save the result to the variable <var> /=<expr>; divide expr value to the variable, save the quotient to the variable <var> %=<expr>; divide expr value to the variable, save the remainder to the variable <var> &=<expr>; take the variable and the expr value as the bit AND, save the result to the variable <var> |=<expr>; take the variable and the expr value or bit OR, save the result to the variable <var> ^=<expr>; take the variable and the expr value or bit XOR, save the result to the variable <var> >>=<expr>; move the variable to the right for expr bits, save the result to the variable <var> <<=<expr>; move the variable to the left for expr bits, save the result to the variable → Increment and decrement operators ++<var> add 1 to the variable and then calculate <var>++ calculate and then add 1 to the variable --<var> subtract 1 from the variable and then calculate <var>-- calculate and then subtract 1 from the variable → Conditional operators <expr> ? <statement1> : <statement2> ; If the <expr> operation result is true, execute <statement1>, or else execute <statement2> → The priority and associativity of operators When multiple operators are included in one operation, the operation priority of the operators should be noted to avoid the result being different to what is expected. The table below lists the operator priority and associativity. Operators in the front column own a higher priority while the priority of those in the same column depend on their associativity. Rev. 1.30 Operators Description Associativity [] () -> sizeof Array element Quotation for Function or operation Indirect access of structure or equation members Byte size of data From left to right ++ -~ ! + & * Add 1 Subtract 1 Take one's complement Deny Unary minus sign Unary plus sign Take the variable address Access the content of the pointer address From right to left * / % Multiplication Division Remainder From left to right + - Addition Subtraction From left to right << >> < <= > >= Move left Move right Smaller than Smaller than or equal to Greater than Greater than or equal to From left to right 23 March 15, 2011 Using Enhanced Holtek C Operators Description Associativity == != & ^ | && || ?: Equal Unequal Bit AND Bit XOR Bit OR Logic AND Logic OR Conditional operation From left to right = *= /= %= += -= <<= >>= &= |= ^= Configuration Multiply, then save to the variable Divide, then save the quotient to the variable Take the remainder, save to the variable Add, then save to the variable Subtract, then save to the variable Move left, then save to the variable Move right, then save to the variable Bit AND, then save to the variable Bit OR, then save to the variable Bit XOR, then save to the variable From left to right Separate variables or expressions From left to right , ■ Type conversions before operation When an operator has operands of different types, they must be converted to a common data type which will be automatically executed by the C compiler. To prevent data loss during conversions, it will be converted to a larger type in general. In some situations, even the operand type is the same, a type conversion will still be executed before the operation. The C compiler will convert automatically according to the operation status. If a conversion is required to be implemented using a definite method, it is acceptable to execute a forced conversion type. (type cast). Example: char count, a = 0 , b = 50 ; if( a – b < 10 ) count++ ; The result from subtraction and comparison by the C compiler show -50, so count++ will be executed. If the operand changes to: if( (unsigned int)(a – b) < 10 ) count++ ; then being converted to an unsigned int with an operation result of 206, count++ will not be executed and the shift operators will operate differently according to the operand type. The right shift operator in the signed char/int/short/long type operation is to copy the most significant bit, MSB, of the original to that of one bit to the right of the MSB. For example: signed int 0x0124 after shifting to right for one bit becomes 0x0092 (MSB=0) signed int 0x8024 after shifting to right for one bit becomes 0xC012 (MSB=1) The right shift operand on the unsigned char/int/short/long type will fill the MSB with 0. The left shift operand to the unsigned/signed type operation will clear the least significant bit to zero. Rev. 1.30 24 March 15, 2011 Using Enhanced Holtek C 4.5 Program flow control After familiarisation with the program architecture, variables and the data types, expressions and operators, the most efficient best program flow can be designed. In the following statements, bold characters represent reserved words which must be used when executing these statements. Using these reserved words as variable names are not permitted. What is grouped in brackets in the syntax is optional, depending upon the actual requirement. 4.5.1 if-else statement Syntax: if ( cond-expression ) statement1 ; [ else statement2 ; ] Description: This control statement is used to determine conditions. Different conditions may require different manipulations and flow control can be executed with this statement. If the result of the conditional cond-expression is true (not zero,) the statement1 will be executed; otherwise if the else part is included, statement2 in the else part will be executed. The brackets indicate that the requirement of this part entirely depends upon the program. statement1 and statement2 may contain more than one statement. If the statements increase, then braces will be needed to group them. For example: if( seconds > 59 ) { minutes++ ; seconds = 0 ; } else seconds++ ; 4.5.2 Switch statements Syntax: switch( expression ) { case constant1 : statement1 ; break ; case constant2 : statement2 ; break ; …… [ default : statementX ; break ; ] } Description: if-else controls the corresponding flow of an expression and has only two results. If the result of an expression exceeds two values, the switch-case statement will help increase the readability of the program and help to better understand its function. If the result of the expression is equal to constant1 then statement1 will then be executed. If the result of the expression is equals to constant2 then statement2 will be executed. If no results are equal to any of the expression then statementX will be executed as a default. The default part is optional. Each case or default may contain several statements, ex: case constant2 : Rev. 1.30 25 March 15, 2011 Using Enhanced Holtek C statement20 ; statement21 ; …… break ; expression can be either a variable or an expression that can calculate a value. A break must be added to end the case within each switch statement. If no break is added then the program will continue to the next executing all statements until a break is encountered. In the example below, if a break is not included, then in case `1', when input_key=1, color=RED will be executed but the next statement color=GREEN will also be executed as there is no break. The result may not be what is expected by the programmer. Example: switch( input_key ) { scase 1 : color = RED ; break ; case 2 : color = GREEN ; break ; …… default : color = WHITE ; break ; } 4.5.3 for statement Syntax: for (initial-expression ; cond-expression ; update-expression) statement1 ; Description: The for statement is to repeatedly execute the same operation (statement) as a loop function. The initial-expression will be firstly executed usually with some variable setup, which will be executed once. If this expression is not present, then the cond-expression comparison will be executed directly. The cond-expression is a conditional expression, and is the same as cond-expression in the if-else. When a condition is satisfied, statement1 will be executed or else it will leave this loop and jump to the statement after statement1. After the condition is satisfied and statement1 executed, the program will continue to the update-expression, then return to cond-expression for comparison and repeat the same execution until the condition is no longer satisfied and the jump out of the loop to the next statement. If update-expression does not exist, the comparison to cond-expression will be directly executed. Statement1 can be more than one statement which must be included in braces. Example: Clear each element in the variable array to zero, int idx ; char cbuf[20] ; for( idx = 0 ; idx < 20 ; idx++ ) cbuf[idx] = 0 ; Rev. 1.30 26 March 15, 2011 Using Enhanced Holtek C 4.5.4 while statement Syntax: while ( cond-expression ) statement1 ; Description: This statement has a similar conditional loop function to the for statement except for the initial-expression and update-expression. Once cond-expression is true, statement1 will be executed until the condition is no longer true and jump out of the loop. statement1 can be more than one statement which must be included in braces. Example: int idx ; idx = 0 ; while( idx < 20 ) { cbuf[idx] = 0 ; idx++ ; } 4.5.5 do-while statement Syntax: do { statement1 ; } while ( cond-expression ) ; Description: The do-while will execute statement1 first and then compare it with the condexpression. If the condition is satisfied, statement1 will be executed again until the condition is no longer true to jump out of the loop. This statement is similar to the while statement except that statement1 will be executed at least once. statement1 can be more than one statement. Example: idx = 0 ; do { cbuf[idx] = 0 ; idx++ ; } while( idx < 20 ) ; 4.5.6 goto statement Syntax: goto label ; Description: goto is a compulsory flow control statement which can directly jump out of a loop switching the program to a statement label or from a switch-case to label. The standard name label and statement goto, are not allowed to force the program to jump to other functions, so therefore must be included in the same function. Example: refer to the 4.5.7 example 4.5.7 break and continue statement Syntax: break ; continue ; Description: break is to jump out from a loop or switch case, one layer at a time. continue is to jump over the statements after a continue to execute the next loop. Example: idx = 0 ; Rev. 1.30 27 March 15, 2011 Using Enhanced Holtek C while( idx < 20 ) { if( cbuf[idx] == 0 ) // cbuf[] no character break ; // jump out while loop idx++ ; } for( idx =0 ; idx < 20 ; idx++ ) { if( cbuf[idx] != 0 ) continue ; // cbuf[] still have character, return idx++, continue goto count_l ; // no character, count total characters } …… // other statements count_l : // label // idx = total characters 4.6 Functions A function is a collection of statements. All statements to be executed must be defined in a function (main() is the main function.) Before using a function, it must be declared and defined or the compiler will generate an error message. In addition to the statement content, the most important part of functions are their arguments and return values. The format is: return-type function_name(var-type arg1, var-type arg2, …) { statements ; } return-type is the type of the function return value. Data types in section 4.2 can be used here. function_name is the name of a function, composed of characters, digits (0~9) and underscores using case-sensitive for respective meanings. var_type is the argument type with arg1, arg2 as the names using case-sensitive for their respective meanings. 4.6.1 Arguments Based on the data type, the arguments are saved using the following method for calling into a function. → All arguments are saved using internal variables. The name of the variable is based on the function plus a digit starting from zero. For example, if the function is named by multiple, the first argument will be saved to variable multiple0 and the second argument will be saved to multiple1 etc. If the argument type is int or unsigned int, two storage bytes will be used. For example, if the second argument is int type, the variable multiple1 will store the low byte of the argument while the variable miltiple1+1 will save the high byte. 4.6.2 Return Values With regard to assembly language: → If the return value is one byte, it will be placed into an accumulator to call back the statements in a function. → If the return value is two bytes (int, short), the low byte will be placed into the accumulator and the high byte in the variable RH. → If the return value is four bytes (long, float), the low byte of the low word will be placed into the accumulator, the high byte of the low word in the variable RH, the low byte of the high word in the variable RM and the high byte of high word in the variable RU. To increase program modularity and readability, the best method is to implement each function using individual routines. The main function will be responsible for the management and calling of individual functions which can be placed into different program files from the main function. Rev. 1.30 28 March 15, 2011 Using Enhanced Holtek C 4.7 Interrupt Service Routines The C language can write the Interrupt Service Routines (ISRs) for interrupts in the microcontroller hardware. The following rules should be noted: → The return value type of the function must be void → No parameters should be included in the function → Use #pragma vector to setup the interrupt vector in the interrupt service routine Use @ to indicate the interrupt vector → Calling the interrupt service routine from other locations is not recommended. → Regarding microcontrollers without nested interrupts, the interrupt function cannot be enabled in the interrupt service routine. Ex: #pragma vector ISR_Timer @ 0x08 void ISR_Timer(void) { } // define: return value type, no arguments, set the interrupt vector to 0x08 The C compiler will check for the required registers used by the interrupt service routine and save the register values after entering the interrupt service routine and then restore the previously saved register values following the execution of the interrupt service routine. Finally it will proceed with execution back from the interrupt and at the same time enable the interrupt function to generate an interrupt. If the interrupt function is required by any other program sections, the corresponding registers can be setup by the user. When the interrupt service routine is processing this interrupt and another urgent interrupt is generated and needs to be handled, the microcontroller must stop the current interrupt service routine and handle the newly generated interrupt. This is in fact the nested interrupt feature. When compiling the interrupt service routine, the Holtek C compiler will save some frequently used register values to the RAM space. After the interrupt service routine has completed it will return to the previous interrupt breakpoint and restore the saved contents back to the registers. To support an interrupt nested event, the Holtek C compiler will save a group of RAM space for each interrupt vector. For example the interrupt service routine in the interrupt vector 1 (INT 1) will save the register contents to V1xx while that in the interrupt vector 2 (INT 2) will be saved to V2xx, and so on. The register contents saved by the interrupt service routine includes accumulator (ACC), status (S), bank pointer (BP), indirect address pointer (MP0, IAR0) and intermediate variables used in actual operations (prefixed by T, ex. T2, T3….) These registers or intermediate variables will be saved to their associated variables, V1A, V1S, V1BP, V1MP0, V1MP1, V1T2, V1T3…(interrupt vector 1), V2A, V2S, V2BP, V2MP0, V2MP1, V2T2, V2T3…(interrupt vector 2) and so on. Microcontrollers without a BP and MP1 need not be saved. Although different interrupt events can be nested, the same interrupt event cannot be nested which means the previous one should be processed first before acknowledging the next interrupt event. It is acceptable to disable the same interrupt in the interrupt service routine. If different interrupts in a microcontroller cannot be nested, the Holtek C compiler can use only a group of memory space to store the register contents yet must be defined in advance in the program of the interrupt service routine (using #pragma novectornest) where the interrupt function will be disabled (disable interrupt, _emi=0). Rev. 1.30 29 March 15, 2011 Using Enhanced Holtek C 4.8 Inline Assembly Codes in C For more simple and effective compiled programs, it is possible to embed assembly code into the C-program. Syntax: #asm [label:] opcode [operands] ... #endasm #asm Description: #asm and #endasm are the preprocessor virtual instructions of the inline assembly code. The C compiler will directly write the assembly codes after #asm (or between #asm and #endasm) to the output file as if it was using the assembly code for program creation. Comment: The embedded inline assembly code can only be located within a function. The embedded inline assembly code can only include instructions and cannot include the definition of variables and sections etc. Any embedded variables must use the associated assembly names. The connection between the variable and the assembly name is as follows: Global Variable: var→ _ var (exceed one byte: _var,_ var[1],_ var[2],_ var[3]) Ex. char a;long b; Associated ASM name: a→_a, b→_b,_b[1],_b[2],_b[3] Local Variable: CR1, CR2, CR3…(exceed one byte: CR1, CR1[1], CR1[2], CR1[3] ) among which the digits are arranged in the order of compilation. Therefore if the defined order is changed, the compiling name will also be changed at the same time while the local variable will not have a corresponding compiling name until it is used. For example, if the r in the above example was not given a value of zero, an error will be reported as CR4 cannot be found. Ex. void fun() { char a;long b; a = 0;b=0; //Associated ASM name: a→CR1, b→CR2,CR2[1],CR2[2],CR2[3] } Function name: name(a,b)→_name(function argument name: name0,name1,name2…) Ex: void fun(char a,char b) {} Associated ASM name: fun→_fun, a→fun0, b→fun1 The variable static should not appear in the embedded assembly as its assembled name is random. If there is no way to reference a variable to its corresponding assembly name, refer to #pragma debug variable 8 2 CR1 d1 in the output asm file. Here d1 is a variable, CR1 is the assembler name corresponding to d1. The compile operation must have completed before the asm file can be generated. Example: char a; int b; void fun(char p1,int p2) { a = p1; #asm // b = p2; Rev. 1.30 30 March 15, 2011 Using Enhanced Holtek C MOV A,fun1 MOV _b,A MOV A,fun1[1] mov _b[1],A #endasm } void main() { int d1; char d2; unsigned char q, r; r = 0; q = d1 / d2; // get quotient #asm // r = q; MOV A, CR3 ; CR3→q MOV CR4,A ; CR4→r #endasm #asm // fun(d2,d1) MOV A,CR2 MOV fun0,A ; p1 = d2 MOV A,CR1 MOV fun1,A MOV A,CR1[1] MOV fun1[1],A ; p2 = d1 CALL _fun #endasm } Rev. 1.30 31 March 15, 2011 Using Enhanced Holtek C 4.9 Preprocessor A preprocessor is a character string prefixed with #. All preprocessors in the program will be initially managed by the preprocessor. These commands provide functions such as define, macro, include file, conditional compiler and pragma. 4.9.1 Define Characters (#define) → #define Syntax: #define sym_name replaced_text Description: Define sym_name as replaced_text which can be a value, expression or alphabetic string. The preprocessor will change all sym_name statements in the program to replaced-text. Example: #define TOTAL_COUNT 40 #define PA0_12_0 → #undef Syntax: #undef sym_name Description: cancel the previously defined characters or preprocessed macros Example: #undef PA0 // cancel the pre-defined PA0 4.9.2 #include → #include Syntax: #include “file_name"or #include <file_name> Description: Combine the designated file into the present program loation. When file_name is placed within in quotes, the compiler will first search for the file in the working directory. If the file is not found, it will continue to search in the current directory or else generate an error message. When file_name is placed within < >, the compiler searches only in the directory setup by the environment parameter. 4.9.3 Inline Assembly → #asm #endasm Syntax: #asm assembly instruction, ex. MOV A, 1 #endasm Description: Locate assembly code commands between #asm and #endasm Example: #asm AND A, 0Fh SUB A, 09h #endasm Rev. 1.30 32 March 15, 2011 Using Enhanced Holtek C 4.9.4 Conditional Compiling (#if/#endif) → #if #else #endif Syntax: #if expression statements1 ; [#else statements2 ; ] #endif Description: Controls conditional compilation. When an expression is true, the program in statements1 will be compiled, otherwise statements1 will be ignored. If there is an #else, then statements2 will be compiled. #else is optional according to actual needs. Example: #if MODE > 0 #define DISP_MODE MODE #else #define DISP_MODE 7 #endif → #ifdef Syntax: #ifdef symbol statements1 ; [ #else statements2 ; ] #endif Description: If symbol is defined in advance (using #define), then statements1 will be compiled, otherwise statements2 will be compiled. #else is optional. → #ifndef Syntax: #ifndef symbol statements1 ; [ #else statements2 ; ] #endif Description: Opposite of #ifdef, if symbol has not been defined previously, then statemens1 will be compiled or else statements2 will be compiled. #else is optional. → #elif Syntax: #if expression1 statements1 ; #elif expression2 statements2 ; #endif Description: If expression is true, statements1 will be compiled, or if expression2 is true, statements2 will be compiled. #elif is the abbreviation for #else #if. Preprocessor Rev. 1.30 Function #asm Indicate to following this line is “in-line assembly" #define Define symbol or preprocess macro #elif Abbreviation for #else #if 33 Example #asm add a, 1 #endasm #define COUNT 20 #define OK #define add(a,b) ((a)+(b)) Refer to #ifdef March 15, 2011 Using Enhanced Holtek C Preprocessor #else #endasm #endif #error Function Define “condition not true" original expression Indicate the end of “in-line assembly" End up “conditional type original expression" Error message occurs #if Define “condition true" original expression If the condition is true, place the subsequent original expression into the program #ifdef If the preprocessed symbols are defined, add the subsequent original expressions #ifndef If the preprocessed symbols are not defined, add the subsequent original expressions #include #pragma #undef Add the contents in the header file Special compiler options Cancel previously defined symbol or preprocess macro Example Refer to #if Refer to #asm Refer to #if #error Size too big #if tick < 10 tick++ ; #else tick = 0 ; #endif #ifdef COUNT delay() ; #else nothing() ; #endif #ifndef COUNT nothing() ; #endif #include “ht46R23.h" Refer to relevant section #undef COUNT 4.9.5 Compiler Special Options pragma Format #pragma keyword [ options ] Some keywords may have options. Keywords for pragma are as follows. keyword bp_free bp_nofree function nobp nolocal nomp0 nomp1 rambank0 norambank options — — V — V — — — rombank0 norombank — rombank vector V V novectornest — Function Description No changes to the BP register in the function ② Cancel the bp_free function ② Set the ROM address at the function location Do not save the BP register contents in the interrupt service routine ② Set function parameter and internal variable as general type, not LOCAL ② Do not save the MP0 register contents in the interrupt service routine ② Do not save the MP1 register contents in the interrupt service routine ② Set the variable to the location address of the RAM bank 0 ③ Cancel the rambank function Set the variable to the location address of ROM bank 0 ① Cancel the rombank function ① Locate a function to a ROM bank ① Define interrupt service routine Only a group of register storage assigned to the interrupt service routine; no interrupt nesting allowed ① Only effective in microcontrollers with multiple ROM banks ② Only effective on microcontrollers with multiple RAM banks and only one ROM bank ③ Only effective on microcontrollers with multiple RAM banks Keyword functions are listed as follows: → #pragma bp_free #pragma bp_nofree Regarding the statements included within these two preprocessors, the compiler will delete all command codes that change the BP register, namely the MOV BP, A commands, so as to reduce the command codes generated by the compiler. Ensure that the memory where variables are located are all within the same RAM bank, starting from the statement after #pragma bp_free and before #pragma bp_nofree. These two preprocessors can be added to include any statement in the function. → #pragma function function_name @rom_address Locate the designated function function_name at rom_address in ROM. Rev. 1.30 34 March 15, 2011 Using Enhanced Holtek C Rom_address can be hexadecimal, ex. 0x100 means to locate the designated function in the 256 ROM addresses. If the function is to be located at address 256 in ROM bank 1, then set rom_address to 0x2100. → #pragma nobp This is only effective in interrupt service routines, indicating that the compiler should not save the BP register contents in the interrupt service routine. The command will be effective in all interrupt service routines in one single program. → #pragma nolocal function_name For general functions. Regarding designated functions, function_name will set the local variables and internal registers used by the function while compiling as general type variables instead of common type whose RAM space is shared by other variables. When the interrupt service routine calls other functions, the local or internal variable data used by these functions may be at risk of being corrupted if they share the same RAM space with other functions. This preprocessor is able to eliminate this risk. → #pragma nomp0 #pragma nomp1 Only effective within interrupt service routines, indicating to the compiler not to save the MP0 or MP1 register contents in the interrupt service routine. One effective for all interrupt service routines in a single program file. The compiler will determine whether to save the BP, MP0, MP1 register content or not in accordance with the microcontroller structure. → #pragma rambank0 #pragma norambank Define the variables to be saved in RAM bank 0. All variables defined between these two preprocessors will all be stored in RAM bank 0. Used when the microcontroller has more than one RAM bank. If the microcontroller has only one RAM bank then this preprocessor is not required. When the variable is a bit type, the variable must be defined in RAM bank 0. → #pragma rombank0 #pragma norombank Locate functions in ROM bank 0. All functions defined within these two preprocessors will be placed in ROM bank 0. → #pragma rombank banknum function_name1 [, function_name2 [, ..]] This preprocessor will locate the designated function in the ROM bank. banknum is the ROM bank number which is set according to the microcontroller ROM structure. The. function_name1, function_name2 are the function names. More than one function can be designated at the same time. → #pragma vector isr_name @vector_address Declare the name of the interrupt service routine and interrupt vector value. A previous declaration is required for each interrupt service routine. isr_name is the function name. vector_address is the interrupt vector value. → #pragma novectornest This means only a group of register memory is reserved in the interrupt service routine. When nested interrupts are not allowed by the microcontroller it must complete the previous processing before handling the next one. This preprocessor can help save RAM space and avoid an overflow from RAM bank 0. Refer to segment 4.7 for the interrupt service routine description. Rev. 1.30 35 March 15, 2011 Using Enhanced Holtek C 4.10 Holtek C built-in Functions ■ Direct assembly compilation built-in functions C Function assembly instruction void _clrwdt( ) CLR WDT void _clrwdt1( ) CLR WDT1 void _clrwdt2( ) CLR WDT2 void _halt( ) HALT void _nop( ) NOP ■ Other built-in Functions void_rr (char *p) sets the pointer p to 1 byte and rotates right one bit ex. ch = 0xA5 ;_rr(&ch) ; then ch = 0xD2 void_rr (int *pl) sets the pointer pl to 2 bytes and rotates right one bit ex. ch = 0xA5A5 ;_rr(&cnt) ; then cnt = 0xD2D2 void_rl (char *p) sets the pointer p to 1 byte and rotates left one bit ex. ch = 0xA5 ;i_rl(&ch) ; then ch = 0x4B void_lrl (int *pl) sets the pointer pl to 2 bytes and rotates left one bit ex. cnt = 0xA5A5 ;_lrl(&cnt) ; then cnt = 0x4B4B void_swap(char *p) sets the pointer p to 1 byte and exchanges the four low bits with the four high bits ex. ch = 0xA5 ;_swap (&ch) ; then ch = 0x5A void_delay(unsigned long tick) delay for tick instruction cycles tick <=263690, if tick =0 it means infinite loop (Refer to segment 5.2.2 description) Rev. 1.30 36 March 15, 2011 Using Enhanced Holtek C Chapter 5 Basic C Language Programs The main point of this chapter is to introduce C-language writing methods using examples. It will start with simple syntax using the microcontroller characteristics and easy to understand frequently used programs. After familiarization with these methods the section moves on to more complex syntax. 5.1 C Syntax Concepts → The equal mark “=" (assignment) in the C statement contains writing, pointing, output functions. Ex. PortAC = 0x01 ; If PortAC is the control register (0x13) of I/O Port A, then the above statement indicates to set bit 0 in Port A to 1 and the other bits to 0. Bits sets to 0 indicate outputs while bits set to 1 indicate inputs. This statement is equal to the following in assembly language: MOV A, 01 MOV PAC, A ;; PAC = [13H] → Use two successive equal marks “==" when using conditional statement and “!=" for the contrary. if( count == 10 ) { } This means if the variable count is equal to 10, then execute the statement within the brackets, or else jump out of the brackets if it is written as if( count = 10 ) therefore “10” will be first written to the variable count, and then checked if count is true (not zero). It must be true in this example and the statement within the brackets will be executed , therefore it may be an error. → Number value syntax and and assembly language difference Hexadecimal values start with 0x followed by 0 to 9 and A to F and are non case-sensitive. Ex. 0x13, 0xad2, 0x2B → The variable or register at the right side of the equal mark “=" contains a read function, Ex. status = PortA ; Read the input value form Port A and save to variable status which functions as the following in assembly language MOV A, PortA ;; PortA = [12H] MOV status, A Rev. 1.30 37 March 15, 2011 Using Enhanced Holtek C 5.2 Loop Application When a program needs to repeat the same or similar actions, using a loop can be of help. For example, clear the contents in the data memory to zero, or setup or clear every bit in the I/O port. Keywords for this syntax include for, while, do-while etc. as the description below shows. 5.2.1 Example 1: 8 Flashing LEDs Functional Description : Use the HT48R06A-1 to control 8 flashing LEDs. Circuit Description : Connect eight pins on Port A in the HT48R06A-1 to the resistor and LED and control the LED from port A. Program Description : The program will setup the eight pins of Port A in turn to illuminate the corresponding LEDs. #include “ht48r06A-1.h" // insert the header file ht48r06a-1.h, defined variables are necessary // _pa is the data port address of Port A, _pac is the control register of Port A. void main(void) // define main function { char status, cnt ; // define local variable as the LED control code _pac = 0 ; // write zero to the control register of Port A, set Port A to be output type for( cnt = 0 ; cnt < 8 ; cnt++ ) // loop, turn on the LED for 8 times in all { status = (1 << cnt) ; // The first LED to is PA0, and PA1 is the second… _pa = status ; // output to Port A, illuminate a single LED and turn off the others } } Try: Change the for loop in the program above to a while loop. 5.2.2 Delay Timing When handling the peripheral devices of the microcontroller, strict timing requests will be made, therefore using C to design a good timing control program will be a great asset in C applications. The following methods can be used to do this:. → Use an in-line assembly method to embed an assembler timing routine into the C program. If using a call function then it is necessary to understand how use the mixed language assembler and C-language together. If using macros then this will increase the program size which is not good for flexible Program Memory space. This implementation method is suitable for applications whose delay timing is shorter than five instruction cycles. → Using C to do the timing control program, it is necessary to calculate the actual operation time of the assembled instructions after compilation. Some timings are however inaccurate and will require some adjustments. This chapter will use C to write a timing control program, analyse it and provide a correct function for the user to easily control the required timing. This function uses instruction codes compiled using the Holtek C Compiler. If using other C compilers, a similar analysis will be necessary however the results may be different. 5.2.2.1 Timing Control Method (1) – using for-loop → Define a timing control program delay void delay (unsigned int cycle) { unsigned int count ; for( count = cycle ; count > 0 ; count-- ) ; } Rev. 1.30 38 March 15, 2011 Using Enhanced Holtek C Input variable cycle to decide the instruction cycle delay, which can be acquired from the microcontroller frequency 1 stands for 26 cycles, 2 for 34 cycles…the assembly program after compiling will be as follows. The calling parameters for delay(count) will be stored in the variable delay0 (low byte) and delay0+1 (high byte) for( count=cycle ; count > 0 ; count-- ) 0000 0700 R MOV A,delay0 0001 0080 R MOV count,A 0002 0700 R MOV A,delay0[1] 0003 0080 R MOV count[1],A 0004 2800 R JMP L5 0005 L2: 0005 0700 R MOV A,count 0006 0A01 SUB A,01h 0007 0080 R MOV count,A 0008 380A SNZ [0AH].0 0009 1580 R DEC count[1] 000A L5: 000A 1080 R SZ count 000B 2800 R JMP L2 000C 1080 R SZ count[1] 000D 2800 R JMP L2 000E L1: 000E 0003 RET // CALL, JMP, RET needs two instruction cycles // SZ skips the next instruction, two instruction cycles will be required. count =1 means to wait for 22 instruction cycles, count =2 means to wait for 30 instruction cycles. 5.2.2.2 Timing Control Method (2) – using while-loop → Define the timing control program delayw void delayw (unsigned int count) { while ( count != 0 ) count-- ; } Changed to use the while loop, the compiled instruction codes are as shown below: 0000 2800 R JMP L8 0001 L7: 0001 0700 R MOV A,count 0002 0A01 SUB A,01h 0003 0080 R MOV count,A 0004 380A SNZ [0AH].0 0005 1580 R DEC count[1] 0006 L8: 0006 1080 R SZ count 0007 2800 R JMP L7 0008 1080 R SZ count[1] 0009 2800 R JMP L7 000A L6: 000A 0003 RET // CALL, JMP, RET need two instruction cycles // SZ, SNZ skips the next instruction, two instruction cycles will be required. count =1 means to wait for 22 instruction cycles, count =2 means to wait for 30 instruction cycles. Rev. 1.30 39 March 15, 2011 Using Enhanced Holtek C 5.2.2.3 Timing Control Method (3)- using built-in function: _delay() void _delay(unsigned long clocks) The parameter clocks is an instruction cycle count delay. For example, if the frequency of the MCU is 4MHz resulting in an execution time of 1us for every instruction cycle, then a valid count within a range of 1 ~ 263690 is allowed. If the valid count is zero, an endless delay is requested and can be used to wait for a hardware interrupt to occur. This function can accurately figure out the exact time and be executed correctly. For exact timing applications, this function can be called. If the required time exceeds the 263690 count limit, this function can be called repeatedly. 5.3 Notes for writing an MCU Application Program a) Modularise the application program by extracting each function in the function format and respectively defining in different files with each function and file named by their functions for easier memorisation. By decreasing the compilation time, less error can be expected in a compiled program with reduced maintenance and modification will also be easier. b) Variables should be defined with realistic meanings so as to avoid errors and make them easier to be used during programming and to reduce maintenance time. c) Do not execute the read/write operations to the following registers as the assembly language instructions compiled by C will use these registers. If the registers are used by the application program at the same time, possible erroneous behavior may affect program execution. C Variable Name (Defined in the header file .h) Register RAM Address IAR0 0x00 _iar0 MP0 0x01 _mp0 IAR1 0x02 _iar1 MP1 0x03 _mp1 BP 0x04 _bp ACC 0x05 _acc PCL 0x06 _pcl STATUS 0x0a _status Additionally, symbols defined in the header file (.h), such as _c, _ac, _z, _ov, _pdf and _to should be avoided. Ex. if(_c) a = ah + bh + 1 would not be allowed. d) When using the Holtek C compiler, if the program needs to use the following built-in functions, the data type of the parameter should be noted and a functional declaration will be required. Ex. extern void _delay(unsigned long) ; _rr(char *) , _rrc(char *), _lrr(int *), _lrrc(int *), _rl(char *), _rlc(char *), _lrl(int *), _lrlc(int *), _swap(char *), _delay(unsigned long) The return type of the above built-in functions are all void. If it is necessary to make a left/right shift on the parameter, the following operators can be used. count = time >> 3 ; count = entry << 4 ; There is no rotate operator in C language. It can be implemented by using the signed type and operator shifting to the left/right. e) Initialise the memory to zero The designer should give every variable an initial value or it will be random. f) 16-bit data is composed of two registers, ex. adrl and adrh of the A/D Converter, which can be saved by a unsigned int type of variable after being read. Rev. 1.30 40 March 15, 2011 Using Enhanced Holtek C unsigned int voltage ; voltage = (unsigned int)_adrh ; voltage = (voltage << 4) + (unsigned int)(_adrl >> 4) ; // 12 bit ADC g) Avoid using goto statement as these will label definitions which restrict program modularisation. An excess of labels will reduce program readability. h) Avoid using #include to add a .c file into the other .c files. The preprocessor #include is used to insert the header file, .h, allowing the C compiler to read defined constants, declared variables and functions etc in the header file as a statement reference for the original program and then to generate the correct instruction codes. If a .c program file is added to another .c program file, which equates to an increase in program file size to that of the two files added together, this will, not only increase the compiling time but also reduce readability and result in less easier maintenance 5.4 Microcontroller Application Program Example The microcontroller application program is required to execute certain operations. The following examples are written in C. Designers can directly add to the program to other subroutines waiting to be called and implement the required operation. a) Initialise the memory space by clearing it to zero. Generally this step will be taken before entering the main program by adding it to the main function main() or construct another subroutine and call it form the main function. Clear the data memory bank 1 to zero. unsigned char *mptr ; for( mptr = (unsigned char *)0x140 ; mptr < (unsigned char *)0x153 ; mptr++ ) *mptr = 0 ; // clear the 0x40 to 0x53 addresses in the RAM bank1 to zero b) Delay Sometimes the application program may need to wait (or delay) for a period of time before proceeding with other operations, so the waiting time should be calculated accurately. Taking a microcontroller with a 4MHz oscillator as an example which will have an instruction cycle time of 1us. If only a few usec of waiting time is required, then it is acceptable to directly call the built-in function _nop(), or the delay() function. The nop() funciton only takes one instruction cycle. If a 2us waiting time is required simply call _nop() twice. If more waiting time is required, directly call the integrated function _delay(). The parameter clocks in the _delay( clocks) function is the unsigned long data type. Clocks = 0 means an endless delay which can be used to wait for an interrupt to occur which is similar to an endless loop; clocks can have a range of 1 to 263690. If this time is exceeded, continuous calling is allowed. c) Table Data Reading Table data is often seen in an application program for operation references which can be read by the array or pointer in the C language. unsigned char WordTable[16] = { 0x41, 0xE7, 0x52, 0x62, 0xE4, 0x68, 0x48, 0xE3, 0x40, 0x60, 0xC0, 0x4C, 0x59, 0x46, 0x58, 0xD8 } ; // define array, display‘0’, ‘1’, ‘2’, …., ‘9’,’A’, ‘b’, ‘C’, ‘d’, ‘E’, ‘F’ int k ; LedPortCtrl = 0 ; // setup the PA Port as output LedPort = 0xff ; // all off for( k = 0 ; k < 16 ; k++ ) // display a total of 16 character bits { LedPort = WordTable[k] ; // turn on the number k word, reading from the table _delay(50000) ; // delay 50ms Rev. 1.30 41 March 15, 2011 Using Enhanced Holtek C } Another method is to use a pointer. The last data 0x00 of the table indicates the end of the table. unsigned char WordTable[17] = { 0x41, 0xE7, 0x52, 0x62, 0xE4, 0x68, 0x48, 0xE3, 0x40, 0x60, 0xC0, 0x4C, 0x59, 0x46, 0x58, 0xD8, 0x00 } ; unsigned char *tptr ; tptr = WordTable ; while( *tptr != 0 ) { LedPort = *tptr++ ; _delay(50000) ; } d) Reading two bytes of data Use the A/D Converter interrupt to read the converted digital data. Take the 9 bits of the HT46R62 as an example: unsigned int AdcValue ; // define a global variable to save the converted value void ADC_ISR( ) // define the A/D Converter interrupt service routine and read/save the converted value { AdcValue = ((unsigned int)_adrh <<1) + ((unsigned int)_adrl >>7) ; // read the data D8~D1 of the high bits and shift left one bit // read the low byte, shift right 7 bits and combine as one int data type, // and save the variable } If the current timer count needs to be read from the clock, take the HT48R70A-1 as an example in which Timer 0 is 16-bit. unsigned int TimerCount ; // save the read timer count _t0on = 0 ; // disable Timer 0 TimerCount = (unsigned int)_tmr0h <<8) + (unsigned int)_tmr0l ; // read the high bit and shift right 8 bits, read the low bit timer count and add the variable of int for saving _t0on = 1 ; // enable Timer 0 and continue counting e) As for variables or registers outside of RAM bank 0, read/write operations can be executed by designating variable addresses, ex. write the value to the address 0x40 in RAM bank 1. unsigned char Bank0140 @ 0x140 ; // define Bank0140 and assign an address unsigned char out ; Bank0140 = 0x20 ; // write 0x20 to the address 0x40 in the RAM bank 1 out = Bank0140 ; // save the 0x40 content in the RAM bank 1 to the variable out 5.5 Points to Note for Microcontroller Application Program Design a) Code Optimization for Multi-ROM-bank MCUs Developing programs for multiple ROM bank microcontrollers will need to include the instruction (MOV BP,A) for switching the BP register to operate in different ROM banks, this will increase the program memory code size. To reduce the program code size, the following two steps can be applied to the program. ■ Locate all called functions in the same ROM bank, ex. #pragma rombank 1 func1, func2, func3 // locate the func1, func2, and func3 to the ROM bank 1. Refer to section 4.9.5. void func1(void) { } void func2(void) { Rev. 1.30 42 March 15, 2011 Using Enhanced Holtek C ...... func1( ) ; // call func1 and assign to the same ROM bank with func2 ...... switch( ) { case xx : } } void func3(void) { func2( ) ; // assign to store in the same ROM bank with func3 if( ) { } else ...... func1( ) ; ...... } ■ Locate all global variables used by the function to ROM bank 0 using #pragma rambank0 #pragma rambank0 char count ; // assign variable count to RAM bank0 #pragma norambank Rev. 1.30 43 March 15, 2011 Using Enhanced Holtek C Chapter 6 Example Program – Basic Level This chapter uses the C language in a basic way to introduce ways develop microcontroller application programs using the simplest program structure and statements. Each example provides frequently used syntax and statements to design control applications and keyboard processing programs for a display, including a 7-segment LED display, LCD display, 4x4 matrix switch input from which some functions can be switched to other applications simply by a few I/O modifications and re-compilation. 6.1 LED Running Light Display 6.1.1 Objective To learn how to use a for/while loop to implement a delay and continuously output data to port A to illuminate eight LEDs in turn. The interval time is 500ms. 6.1.2 Peripheral Components Connect the I/O Port A in the HT48R06A-1 to the cathode of the eight LEDs via current limiting resistors allowing the MCU to illuminate the LEDs by outputting a low level to the connected PA port. 6.1.3 Circuit Diagram 4MHzPA0~PA7 connected to an LED cathode. The MCU oscillator frequency is 4MHz. 6.1.4 MCU Configuration Options Select the ICE internal oscillator to have a frequency of 4000KHz WDT : disable WDT Instruction : one clear instruction PA wake up : none Pull high : all WDT OSC : on chip RC OSC : crystal Sys volt : 5.000V Sys freq : 4000kHz, Internal Rev. 1.30 44 March 15, 2011 Using Enhanced Holtek C 6.1.5 Program Flow Start Set Port A as outputs with high level to extinguish LEDs Turn on PA LEDs in sequence End 6.1.6 Original Program 1 #include “ht48r06a-1.h” 2 #define LedPort _pa // pa port 3 #define LedPortCtrl _pac // pa control port 4 void main(void) // main function 5 { 6 int k ; 7 LedPortCtrl = 0 ; // set pa port as output 8 LedPort = 0xff ; // turn off all LEDs 9 for( k = 0 ; k < 8 ; k++ ) 10 { 11 LedPort = ~ (1 << k) ; // illuminate the number k LED 12 _delay(250000) ; // delay 500ms 13 _delay(250000) ; 14} 15 16 LedPort = 0xff ; // turn off all LED } 6.1.7 Program Description 1 Insert the header file. It is better to add this statement at the beginning of each program file telling the C compiler to include these file declarations and definitions. 2~3 Define the variable LedPort as the data register and LedPortCtrl as the control register for Port A. 7 Write 0 to the Port A control register to setup port A as outputs. This statement has the same function as the assembly instruction: CLR PAC 8 Write 0xff to the port A data register to output 11111111b which ensures all LEDs are off. 9 The for loop is to illuminate each LED in turn starting with PA0 to PA7. 11 Shift 1 to left for k bits. The letter k stands for the Port A pin number, where k = 0, 1, 2,…7. Then execute a 1's complement to change the 1 to 0, and the other seven 0's to 1 and then write to the number k pin of the Port A data register to setup a low level output to turn on the related LED. The other pins will be high and the LEDS will remain off. 12~13 Generate a 500ms delay to keep the LED on and proceed to illuminate the next LED. The on/off interval of the LEDs will be larger than 500ms as the for loop needs to execute the previous operation before illuminating the LED. 15 Rev. 1.30 Turn off all LEDs. If a second round of LED sequential illumination is required, modify line 15 to "goto again" and add "again": (with a colon) before line 9 or while(1) in line 14 to make an endless loop. 45 March 15, 2011 Using Enhanced Holtek C 6.2 LED Blinking Light 6.2.1 Objective To learn how to use the loop execution delay and continuously output data to Port A to illuminate eight LEDs in turn. The illumination order is read from the array with the following sequency: LED0, LED0+LED1, LED0+LED1+LED2 until all eight LEDs are on. The on/ off interval is 500ms. This shows how to use the an array variable in C instead of using an assembly program to read data using the table read instructions (TABRDC, TABRDL) which increases program readability. 6.2.2 Peripheral Components Connect the I/O Port A vial current limiting resistors to the cathode of eight LEDs so as to turn on the LED by outputting a low level to the connected PA port. 6.2.3 Circuit Diagram PA0~PA7 is connected to the LED cathode. The MCU oscillator frequency is 4MHz. 6.2.4 MCU Configuration Options Select the ICE internal oscillator with a frequency of 4000KHz. WDT : disable WDT Instruction : one clear instruction PA wake up : none Pull high : all WDT OSC : on chip RC OSC : crystal Sys volt : 5.000V Sys freq : 4000kHz, Internal Rev. 1.30 46 March 15, 2011 Using Enhanced Holtek C 6.2.5 Program Flow Start Set Port A as outputs with high level to extinguish LEDs Read array data and turn on LEDs End 6.2.6 Original Program 1 #include “ht48r06a-1.h" 2 #define LedPort _pa // pa port 3 #define LedPortCtrl _pac // pa control port 4 char LedTable[8] = { 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00 } ; 5 // array variable, setup display data, turn on LED0, LED0+LED1, LED0+LED1+LED2, …. 6 void main(void) // main function 7 { 8 int k ; 9 LedPortCtrl = 0 ; // set the pa port as output 10 LedPort = 0xff ; // turn off all LED 11 for( k = 0 ; k < 8 ; k++ ) 12 { 13 LedPort = LedTable[k] ; // output LED control code to turn LEDx 14 _delay(250000) ; // interval of 250ms 15 _delay(250000) ; // interval of 250ms 16 } 17 LedPort = 0xff ; // turn off all LED 18 } 6.2.7 Program Description All program content is the same as section 6.1 except for line 13. Line 13 is to acquire the displayed data from the array LedTable, and then output it to the LED port. Lines 14 and 15 are for the internal function _delay() calling. Parameter 250000 means the delay clock value is 250000. In this example, 1 clock = 1us with each call requiring a 250ms delay. Rev. 1.30 47 March 15, 2011 Using Enhanced Holtek C 6.3 Single 7-segment Display 6.3.1 Objective To display sixteen characters from 0~9 and A~F using a common anode seven-segment display with an interval of 50ms. This example is similar to the earlier example except for the array content. The display code characters must be setup according to seven-segment display structure. 6.3.2 Peripheral Components Connect the I/O port pa0 ~ pa7 of the microcontroller to the seven-segment display. The names of the display code are listed in the following tables. Output low to some segment to turn it on. a f g b p d Character Code Character Code Segment c e Port a b c d e f g p PA2 PA3 PA4 PA7 PA5 PA1 PA0 PA6 0 1 2 3 4 5 6 7 8 9 41h E7h 52h 62h E4h 68h 48h E3h 40h 60h A b C d E F — — — — C0h 4Ch 59h 46h 58h D8h — — — — 6.3.3 Circuit Diagram 6.3.4 Microcontroller Configuration Options Select the ICE internal oscillator to have a frequency of 4000KHz WDT : disable WDT Instruction : one clear instruction PA wake up : none Pull high : all WDT OSC : on chip RC OSC : crystal Sys volt : 5.000V Sys freq : 4000kHz, Internal Rev. 1.30 48 March 15, 2011 Using Enhanced Holtek C 6.3.5 Program Flow Same as the 6.2.5 program flow 6.3.6 Original Program 1 #include“ht48r06a-1.h" 2 #define LedPort_pa // pa port 3 #define LedPortCtrl_pac // pa control port 4 unsigned char WordTable[16] = { 0x41, 0xE7, 0x52, 0x62, 0xE4, 0x68, 0x48, 0xE3, 5 0x40, 0x60, 0xC0, 0x4C, 0x59, 0x46, 0x58, 0xD8 } ; 6 // define array, display `0' , `1' , `2' , …, `9' , `A' , `b' , `C' , `d' , `E' , `F' 7 void main(void) // main function 8 { 9 int k ; 10 LedPortCtrl = 0 ; // set pa port as output 11 LedPort = 0xff ; // extinguish all 12 for( k = 0 ; k < 16 ; k++ ) // display 16 characters totally 13 { 14 LedPort = WordTable[k] ; // turn on the number k character 15 _delay(50000) ; // delay 50ms 16 } 17 LedPort = 0xff ; // extinguish all 18 } 6.3.7 Program Description 14 Read the display data from the WordTable array and then output it to the LED port. 6.4 5*5 Dot Matrix LED Display 6.4.1 Objective To control a 5x5 dot matrix LED to display “HOLTEK” with an interval of 50ms, and each character displayed for 24ms on five rows. This example uses a pointer and an array to read the display elements. The example provided here can be used to read 2-dimensional data. 6.4.2 Peripheral Components Connect PA0~PA4 to the dot matrix array, row0~row4, and PB0~PB4 to column0~4 using the 25 LEDs arranged as a 5x5 array, as the figure shows, and then connect it to Port A and Port B of the microcontroller. ●○○○●○●●●○●○○○○●●●●●●●●●●●○○○● ●○○○●●○○○●●○○○○○○●○○●○○○○●○○●○ ●●●●●●○○○●●○○○○○○●○○●●●●○●●●○○ ●○○○●●○○○●●○○○○○○●○○●○○○○●○○●○ ●○○○●○●●●○●●●●●○○●○○●●●●●●○○○● Rev. 1.30 49 March 15, 2011 Using Enhanced Holtek C 6.4.3 Circuit Diagram 6.4.4 Microcontroller Configuration Options Select the ICE internal oscillator to have a frequency of 4000KHz WDT : disable WDT Instruction : one clear instruction PA wake up : none Pull high : all WDT OSC : on chip RC OSC : crystal Sys volt : 5.000V Sys freq : 4000kHz, Internal 6.4.5 Program Flow Start Set Port A, B as output types Extinguish the 5x5 dot matrix LEDs Extract a pointer from the table to read the the character code Start from the first row, total of 5 in all Turn on one row and 5 columns of the row NO YES Already 5 rows? Rev. 1.30 50 End March 15, 2011 Using Enhanced Holtek C 6.4.6 Original Program 1 #include “ht48r06a-1.h" 2 #define LedRowPort _pa // row, pa port 3 #define LedRowPortCtrl _pac // pa control port 4 #define LedColPort _pb // column, pb port 5 #define LedColPortCtrl _pbc // pb control port 6 char charH[5] = { 0x1f, 0x4, 0x4, 0x4, 0x1f } ; // high level means illuminating 7 char charO[5] = { 0x0e, 0x11, 0x11, 0x11, 0x0e } ; 8 char charL[5] = { 0x1f, 0x10, 0x10, 0x10, 0x10 } ; 9 char charT[5] = { 0x01, 0x01, 0x1f, 0x01, 0x01 } ; 10 char charE[5] = { 0x1f, 0x15, 0x15, 0x15, 0x11 } ; 11 char charK[5] = { 0x1f, 0x4, 0x4, 0x0a, 0x11 } ; 12 char *LedTable[6] = { charH, charO, charL, charT, charE, charK } ; // pointer array 13 14 void main(void) // main function 15 { 16 int k ; 17 char *tptr ; 18 LedRowPortCtrl = 0 ; // set pa port as output 19 LedColPortCtrl = 0 ; // set pb as output 20 LedColPort = LedRowPort = 0 ; // turn off LED 21 for( k = 0 ; k < 6 ; k++ ) // display all 6 characters in all 22 { 23 tptr = LedTable[k] ; // read the pointer of the number k character 24 for( j = 0 ; j < 5 ; j++ ) 25 { 26 LedColPort=(1 << j );//turn on column j of number k character 27 LedPort=tptr[j];//output row character and turn on the related character 28 _delay(5000) ; // every row has an interval of 5ms 29 } 30 _delay(50000) ; // every character has an interval of 50ms 31 } 32 LedColPort = 0 ; // turn off all LEDs 33 } 6.4.7 Program Description 6 ~ 11 Define the six character “HOLTEK” pattern Rev. 1.30 12 The array LedTable defines the pointer of the above six arrays 21 From the first character, six characters in all 23 Extract the character pointer from the array LedTable 24 Each character has a 5x5 dot matrix, displayed by row 26 Output the displayed row data 27 Output the five column data of this row 28 The interval for each row is 5ms 30 The interval for each character is 50ms 51 March 15, 2011 Using Enhanced Holtek C 6.5 HT1621 LCD Control using the HT48 Series of MCUs 6.5.1 Objective The example here uses the HT48 series of MCUs to control the HT1621 LCD controller by sending instructions to the HT1621 to control the LCD panel characters using HT48 I/O ports connected to the HT1621. This example uses the HT48R10A-1 MCU to control the HT1621 and introduces a method of turning on and clearing all the LCD pixels 6.5.2 Peripheral Components The HT1621 is a 128-bit LCD controller whose internal RAM directly corresponds to LCD displayed data. It can used to control LCD panels or for software controlled multi-functional application display systems. The interface between the MCU and the HT1621 requires four to five lines and the internal power saving mode greatly reduces its power consumption. ■ Data Memory The HT1621 LCD driver includes a display data memory of 32x4 bits, arranged as follows. C O M 3 C O M 2 C O M 1 C O M 0 S E G 0 0 S E G 1 1 S E G 2 2 S E G 3 3 S E G 3 1 3 1 D 3 D 2 D 1 D 0 A d d r e s s 6 b its (A 5 , A 4 , ..., A 0 ) A d d r D a ta D a ta 4 b its (D 3 , D 2 , D 1 , D 0 ) There are 32 segments and 4 columns in all. When the LCD is enabled, data is written into this memory to respectively control the associated pixels to be either on or off. D0 controls whether the cross points of common 0 and segment 0 ~ segment 31 are illuminated or not while D1 controls those of common 1 and segment 0 ~ segment 31, D2 controls those of common 2 and segment 0 ~ segment 31, and D3 controls the those of common 3 and segment 0 ~ segment 31. ■ Instruction Format The HT1621 LCD controller supports two modes, the command mode and the data mode. The common mode is used to enable/disable the LCD display and setup the structure of the LCD display. Binary codes starting with 100 is the ID code for the command mode. The data mode is to read/write data from/to the LCD memory, the ID code in this case is 110 or 101. The following table describes the commands used in this example. For details refer to HT1621 datasheet. Rev. 1.30 52 March 15, 2011 Using Enhanced Holtek C ■ Command Mode Command ID Command Code Function Power Reset SYS DIS 100 0000-0000-x Disable system oscillator and LCD bias generator V — SYS EN 100 0000-0001-x Enable system oscillator LCD OFF 100 0000-0010-x Disable LCD bias generator V LCD ON 100 0000-0011-x Enable LCD bias generator — 0010-abxc-x Set LCD bias and common options c=0, bias=1/2 c=1, bias=1/3 ab=00, 2 common ab=01, 3 common ab=10, 4 common — BIAS& COM 100 ■ Data Mode Command ID Command Code Function READ 110 A5A4A3A2A1A0D0D1D2D3 Read data from RAM WRITE 101 A5A4A3A2A1A0D0D1D2D3 Write data to RAM READ-MODIFYWRITE 101 A5A4A3A2A1A0D0D1D2D3 Read/write data to RAM x : don't care bit A5 ~ A0 : RAM address D3 ~ D0 : RAM data Before using the LCD, two commands, SYS EN and LCD ON, should be sent to the HT1621 to enable the LCD. Using serial communication, all commands must be sent bit by bit in the following order: first send the ID (3 bits) and then the command code (9 or 10 bits) and finally pull the /CS level high at the end. Repeat this sequence, and send the next command. If the subsequent command is the same command and a successive address, it is acceptable not to pull the /CS level high but directly continue with reading/writing data until all the data is processed. ■ Command Timing ● READ Mode (Command ID Code : 110) ● READ Mode (Success Address Reading) C S W R R D D A T A Rev. 1.30 1 1 0 A 5 A 4 A 3 A 2 A 1 A 0 D 0 D 1 D 2 D 3 D 0 D 1 D 2 D 3 D 0 D 1 D 2 D 3 D 0 D 1 D 2 D 3 D 0 M e m o ry A d d re s s (M A ) D a ta (M A ) D a ta (M A + 1 ) D a ta (M A + 2 ) D a ta (M A + 3 ) 53 March 15, 2011 Using Enhanced Holtek C ● WRITE Mode (Command ID Code: 1 0 1) ● WRITE Mode (Success Address Writing) C S W R 1 D A T A 1 0 A 5 A 4 A 3 A 2 A 1 A 0 D 0 D 1 D 2 D 3 D 0 D 1 D 2 D 3 D 0 D 1 D 2 D 3 D 0 D 1 D 2 D 3 D 0 M e m o ry A d d re s s (M A ) D a ta (M A ) D a ta (M A + 1 ) D a ta (M A + 2 ) D a ta (M A + 3 ) ● READ-MODIFY-WRITE Mode (Command ID Code: 101) ● READ-MODIFY-WRITE Mode (Success Address Accessing) C S W R R D 1 D A T A 1 0 A 5 A 4 A 3 A 2 A 1 A 0 D 0 D 1 D 2 D 3 D 0 D 1 D 2 D 3 D 0 D 1 D 2 D 3 D 0 D 1 D 2 D 3 D 0 D 1 D 2 D 3 D 0 M e m o ry A d d re s s (M A ) D a ta (M A ) D a ta (M A ) D a ta (M A + 1 ) D a ta (M A + 1 ) D a ta (M A + 2 ) ● Command Mode (Command Mode ID: 100) C S W R D A T A Rev. 1.30 1 0 0 C 8 C 7 C 6 C 5 C 4 C 3 C 2 C 1 C 0 C 8 C 7 C 6 C 5 C 4 C 3 C 2 C 1 C 0 C o m m a n d 1 C o m m a n d ... C o m m a n d i 54 C o m m a n d o r D a ta M o d e March 15, 2011 Using Enhanced Holtek C ● Command Mode (Data and Command Code) 6.5.3 Circuit Connect the HT48R10A-1 I/O ports to the HT1621 pins as shown below: HT48R10A-1 I/O port HT1621 pin PB1 DATA PB2 /WR PB3 /CS PB4 /RD 6.5.4 Configuration Option HT48R10A-1 configuration option settings: WDT clock source : disable OSC : Ext. Crystal Pull-high PA : Pull-high Pull-high PB : Pull-high Input type PA : Schmitt Trigger BZ/BZ : Disable fSYS = 4M 6.5.5 Program Flow Start Set LCD in Normal status Set LCD with SYS EN enabled Set LCD with 4 commons, 1/3 bias Set LCD On (enabled) Initialise the LCD Clear LCD RAM data (turn off LCD) LCD) Write to all LCD RAM space Turn on LCD Read the segment 4 data from LCD RAM. Check if it is the same (0x0f) as the above written content End Rev. 1.30 55 March 15, 2011 Using Enhanced Holtek C 6.5.6 Original Program 1 #include “ht48r10a-1.h" 2 #define CMD_NORMAL 0xE3 // normal command 3 #define CMD_SYSEN 0x01 // system enable 4 #define CMD_SYSDIS 0x00 // system disable 5 #define CMD_LCDOFF 0x02 // LCD off 6 #define CMD_LCDON 0x03 // LCD on 7 #define csb_pb3 // the CSB pin of the HT1621 = bit 3 of port B 8 #define wrb_pb2 // the WRB pin of the HT1621 = bit 2 of port B 9 #define rdb_pb4 // the RDB pin of the HT1621 = bit 4 of port B 10 #define ldata_pb1 / the DATA pin of the HT1621 = bit 1 of port B 11 #define ldata_ctrl_15_1 // bit 1 of port B controls the DATA I/O 12 13 void DelayTime(unsigned int count) // 10 * count + 11, if count > 256 14 { 15 while( count != 0 ) count-- ; 16 } 17 void nop(void) // calling this function will take five instruction cycles 18 { 19 20 asm(“NOP") ; } 21 void SendCommand(unsigned char cmd) // send command to the HT1621 22 { 23 unsigned int CmdCode ; 24 int idx ; 25 // compose command ID 100b and command code into 12 bits 26 CmdCode = ((unsigned int)100b << 9) | ((unsigned int)cmd <<1) ; 27 ldata_ctrl = 0 ; // set port B bit 1 (DATA) as output type 28 csb = 0 ; // CSB pull low 29 for ( idx = 11 ; idx >= 0 ; idx-- ) 30 { if( (CmdCode & (1 << idx)) !=0 ) // check if every bit is 1 or 0, from bit11~bit0 31 32 ldata = 1 ; // write 1 33 else 34 ldata = 0 ; // write 0 35 36 wrb = 0 ; // WRB -> low -> high 37 _delay(5) ; // delay 5 ms (5 instruction clock) 38 wrb = 1 ; 39 _delay(5) ; 40 } 41 csb = 1 ; // CSB pull high, end and send command 42 } 43 // change bit0~bit3 in the odata to bit3~bit0. Ex. change 1110 to 0111 44 char SwapBit(char odata) 45 { 46 Rev. 1.30 char ndata ; 56 March 15, 2011 Using Enhanced Holtek C 47 int k ; 48 ndata = 0 ; 49 for( k = 0 ; k < 4 ; k++ ) 50 ndata |= ((odata >> k) & 0x1) << (3- k) ; 51 return (ndata) ; 52 } 53 // write the lowest 4 bits in the dataN to LCD RAM address (bit0~bit5 effective) 54 //bit 0, 1, 2, 3 of dataN is the com0, com1, com2,com3 data 55 void WriteLCD(char addr, char dataN) 56 { 57 unsigned int DataCode ; // 16 bits 58 char NewData ; 59 int idx ; DataCode = ((unsigned int)101b << 6) | (addr & 0x3f) ; 60 // combine addr 6 low bits into DataCode 61 NewData = SwapBit(dataN) ; // change bit0~bit3 in dataN to bit3~bit0 62 DataCode = (DataCode<<4) | NewData ; combine four low bits into dataN 63 ldata_ctrl = 0 ; // set port B bit 1 (DATA) as output type 64 csb = 0 ; // CSB pull low 65 for (idx = 12 ; idx >=0 ; idx--) // send 13 bits 66 { 67 if( (DataCode * (1 << idx)) !=0 ) // check if every bit is 1 or 0 from bit12~bit0 68 ldata = 1 ; // write 1 69 else 70 ldata = 0 ; // write 0 71 72 wrb = 0 ; // WRB -> low -> high 73 73 74 wrb = 1 ; 75 _delay(5) ; 76 } 77 _delay(5) ; // delay 5 ms (5 instruction clocks) csb = 1 ; // CSB pull high, end and send command 78 } 79 // read a nibble (4 bits) from the LCD RAM addr (bit0~bit5 effective) and send back 80 // the returned values bit 0, 1, 2, 3 is com0, com1, com2, com3 81 char ReadLCD(char addr) 82 { 83 unsigned int DataCode ; // 16 bits 84 char retcode ; 85 int idx ; 86 87 DataCode = ((unsigned int)110b << 6) | (addr & 0x3f) ; // combine addr 6 low bits into DataCode Rev. 1.30 88 ldata_ctrl = 0 ; // set port B bit 1 (DATA) as output type 89 csb = 0 ; // CSB pull low 90 for (idx = 8 ; idx >=0 ; idx--) // send 9 bits 57 March 15, 2011 Using Enhanced Holtek C 91 { if( (DataCode & (1 << idx)) !=0 ) // check if every bit is 1 or 0 from bit8~bit0 92 93 ldata = 1 ; // write 1 94 else 95 ldata = 0 ; // write 0 96 97 wrb = 0 ; // WRB -> low -> high 98 _delay(5) ; // 5 us delay (5 instruction clock) 99 wrb = 1 ; 100 _delay(5) ; 101 } 102 // read 4 bits 103 ldata_ctrl = 1 ; // set port B bit 1 (DATA) as input type 104 retcode = 0 ; 105 105 106 { for( idx = 0 ; idx < 4 ; idx++ ) // idx=0 read D0, iex=1 read D1… 107 rdb = 0 ; // RDB pull low 108 _delay(5) ; 109 rdb = 1 ; // RDB pull high 110 if( ldata == 1 ) retcode |= (1 << idx) ; // read one bit if it is 1 111 } 112 csb = 1 ; // CSB pull high, end and send command 113 return retcode ; 114 } 115 // clear the content in LCD RAM to 0 116 void CleanLCD(void) 117 { 118 char seg ; 119 for( seg = 0 ; seg < 32 ; seg++ ) 120 WriteLCD(seg, 0) ; 121 } 122 // turn on all LCD 123 void DisplayLCD(void) 124 { 125 char seg ; 126 for( seg = 0 ; seg < 32 ; seg++ ) 127 128 WriteLCD(seg, 0xf) ; // turn on com0, com1, com2, com3 } 129 Rev. 1.30 130 void main(void) 131 { 132 char DataR ; 133 _pb = 0 ; // port B data is 0 134 csb = 1 ; // CSB pull high – disable LCD 135 _pbc = 0 ; // set port B as output 136 while(1) // endless loop, jump out only when an error occurs 137 { 58 March 15, 2011 Using Enhanced Holtek C 138 SendCommand(CMD_NORMAL) ; // send NORMAL command 139 SendCommand(CMD_SYSEN) ; // send SYS EN command 140 SendCommand(0x29) ; // set LCD to 4 commons and 1/3 bias 141 SendCommand(CMD_LCDON) ; turn LCD on 142 CleanLCD( ) ; // clear LCD RAM to 0, all pixels are off 143 DisplayLCD( ) ; // turn on all pixels, wirte 0xf to LCD RAM 144 145 DataR = ReadLCD(4) ; // read the segment 4 data 146 if ( DataR !=0x0f) // read and written data are different, error 147 break ; // jump out of loop 148 SendCommand(CMD_LCDOFF) ; // send LCD OFF command 149 150 } } 6.5.7 Program Description 2~6 Define the command constants for the HT1621, namely the 8 bits after the initial100 command code. 7~11 Define the HT48R10A-1 I/O lines which connect to the HT1621 pins. 17~20 Function nop() uses five instruction cycles which can be used for a delay according to the system frequency. 21~42 Function SendCommand is to send commands to the HT1621. Parameter cmd is the command code. 26 100b This is the command ID. Three bits are shifted left for 9 bits to bits 11~9 plus the input parameter cmd is shifted left for one bit to bits 8~1, a total of eight bits. Finally bit 0 can have any value. This line ensures it has a zero value. 27 Setup the DATA pin as an output type. 28 Pull the CSB pin to a low level. 29~40 Output the 12 bits command to the HT1621 starting with bit 11. 31~34 If the bit value is 1, output 1; 0 then output 0. 36~39 Pull the WRB control bit to a low level and then high again. Write the above data to the HT1621. An internal function_delay(5) is available for delaying 5 ms. However note that the actual time is determined by the MCU oscillator frequency. 41 CSB Pull the pin high to end the command transmission. 44~52 Function SwapBit is to exchange the parameter input bits 0~3 to bits 3~0. For example the bits 1110 will be changed to 0111. The point here is that the data is transformed into the format required by the HT1621.This is in a transmission order starting from D0, D2, D2 and D3. 49 Four bits for shifting 50 Move bit 0 to bit 3, bit 1 to bit 2, bit 2 to bit 1, bit 3 to bit 0. 55~78 Write the lowest four bits of the input parameter dataN to the HT1261 RAM addr. Only bit0~bit5 in the addr is effective. Bit0~3 in the DataN is com0, com1, com2, com3 data. Rev. 1.30 60 The variable DataCode is to output the command. First shift the command ID, 101b, to the left for 6 bits plus the lower 6 bits of addr 61 Convert the input parameter dataN bit0~bit3 to bit3~bit0 according to the command format. 62 Shift the DataCode in column 60 to the left for 4 bits plus the data of column 61, to prepare the output data for the HT1621 RAM. 63 Setup bit 1 (DATA pin) of Port B as an output. 59 March 15, 2011 Using Enhanced Holtek C 64 Pull CSB to a low level. 65~78 Write the 13 bits from bit 12~bit 0 to the HT1621 RAM 67~70 If the bit value is 1, then output a 1; if 0 then output 0. 72~75 Pull WRB pin to a low level, delay, and then pull high to write this bit 77 Pull CSB to a high level to end the command transmission. 81~114 Function ReadLCD is to read a byte from addr in the HT1621 RAM. Only the lowest six bits (bit0~bit5) in addr are effective. Only four of the read back bits of the returned value is effective. 87 Generate the output value DataCode by shifting the command ID 110 for six bits plus the lowest six bits in addr. 88 Setup bit 1 (DATA pin) of Port B as an output. 89 Pull CSB to a low level. 90 Transmit the nine bits, including the ID 110 and the 6 bit address 92~95 Check whether each bit is 1 or 0 and then transmit. 97~100 Pull the WRB pin to a low level and then pull high to complete the transmission. 103 Set bit 1 (DATA) of Port B as an input. 105 Read four bits. idx=0 read D0, idx=1 read D1… 107~109 Pull RDB low, delay and then pull high. 110 Save the read bit to the variable etcode |=(1 << idx) ; // read one bit, if it is 1. 112 Pull CSB to a high level to end the command transmission. 113 Return retcode 116~121 Function CleanLCD will clear the LCD RAM contents to zero. The LCD is off. 119 Starting from the first segment (seg=0), clear to zero until the 32nd segment 120 Write 0 to one segment, and clear to 0. 123~128 Function DisplayLCD will write 1 to the LCD RAM and turn on all commons and segments 126 Starts from the first segment until the 32nd segment. 127 Write 1 to all commons of the LCD RAM segments. 130~150 Main function. Controls HT1621 LCD functions such as enable, turn off/on and to check if the read/write operations to the HT1621 RAM is correct. 133 Port B data is 0. 134 Pull CSB to a high level. Disable the HT1621. 135 St port B as output type. 136 Endless loop which repeats operation from column 138 to 148 and jumps out only when an error occurs. 138 Send NORMAL command to the HT1621 139 Send SYS EN command to the HT1621. Enable the system. 140 Setup the LCD to 4 commons and 1/3 bias. 141 Enable the LCD 142 Clear the LCD RAM to zero. All pixels are off. 143 Turn on all pixels. Write 0x0f to the LCD RAM. 145 Read Segment 4 data. 146~147 If the read and write data are different, error, jump out of the endless loop. 148 Rev. 1.30 Send LCD OFF command. 60 March 15, 2011 Using Enhanced Holtek C 6.6 Using the HT48 to control the LCD panel 6.6.1 Objective The objective here is to use the HT48 series device to control the DV16100NRB LCD display driver. This LCM module uses the internal Hitachi HD44780 for drive and control. The example here utilises the C language to have the microcontroller generate the correct signals to meet the timing requirements of the LCM and to display characters. Some functions in this example can be used as common functions which can be called formulas for applications that use the LCM to display. For detailed instruction and timing data, please refer to the LCM related information. 6.6.2 Peripheral Components To use the HT48R10A-1 to control the an LCM character display, the I/O ports PB0~PB7 should be connected to D0~D7 on the HD44780 for data transmission. Port C is used to control the LCM with PC0 connected to pin E for the LCM for enable (E=1) and disable (E=0.) Port PC1, connected to R/W, controls the LCM reading (R/W=1) and writing (R/W=0.) Port PC2, connected to RS, controls the LCM register select to be either an instruction register (RS=0) or data register (RS=1.) If the application circuit uses other I/O ports to connect to the HD44780, only the #define statements need to be modified. HT48R10A-1 Pin HD44780 Pin PC0 E Description Enable control, E=1 (enable), =0 (disable) PC1 R/W Read (=1), Write (=0) PC2 RS LCM register select: 0 = instruction register, 1 = data register PB0~PB7 D0 ~ D7 Data line → Before executing a read/write to the HD44780, it is necessary to first check if the LCD is busy. This can be done by using the busy flag (BF) and address (AC) command to read the instruction register (IR) contents (RS=0, R/W=1). → The flow for a single byte LCD read is as follows. Set RS = 1 to read from the data register or RS =0 to read from the instruction register. Set R/W = 1 Set E = 1 to enable the LCD Read data from D0~D7 and save. Set E = 0 to disable the LCD → The flow for a single byte LCD write dr11 data to the LCD is as follows. Set RS = 1 to read from the data register or RS =0 to read from the instruction register. Set R/W = 0 Set E = 1 to enable the LCD Locate data to D0~D7 Set E = 0 to disable the LCD Rev. 1.30 61 March 15, 2011 Using Enhanced Holtek C 6.6.3 Circuit Diagram Using the MCU to control the LCM character display via I/O pins PB0~PB7 6.6.4 Configuration Option WDT clock source : disable OSC : Ext. Crystal Pull-high PA : Non-Pull-high Pull-high PB : Non-Pull-high Pull-high PC : Non-Pull-high Input type PA : Schmitt Trigger BZ/BZ : Disable fSYS = 4M 6.6.5 Program Flow Start 1.Setup PC0/PC1/PC2 as outputs 2.Setup LCD for 8-bit control 3.LCD ON/OFF setup: display all data 4.Setup entry mode: automatic AC increment 5.Clear display - address counter is 0 LCD initialization Setup the address counter to the first column From 'A' to 'p' - write into LCD DD RAM Setup the address counter to the second column From 'a' to 'p' - write into LCD DD RAM End Rev. 1.30 62 March 15, 2011 Using Enhanced Holtek C 6.6.6 Original Program 1 #include “ht48r10a-1.h" 2 #define LcdData _pb // LCD D0~D7 = PB0~PB7 3 #define LcdDataCtrl_pbc // PB port control register , data is output or input 4 #define LcdE_pc0 // E control pin = PC0 5 #define LcdRW_pc1 // R/W control pin = PC1 6 #define LcdRS_pc2 // RS control pin = PC2 7 #define LcdPortCtrl_pcc // PC port control register 8 #define CMD_READ 1 // R/W constant, read 9 #define CMD_WRITE 0 // RW constant, write 10 #define REG_IR 0 // RS constant, instruction register IR 11 #define REG_DR 0 // RS constant, data register DR 12 #define ENABLE 1 // E constant, enable LCD 13 #define DISABLE 0 // E constant, disable LCD 14 // check if LCD is still busy. 15 // endless loop if the LCD malfunctions? 16 void CheckLCDBusy(void) 17 { 18 LcdDataCtrl = 0xff ; // set DATA as input type 19 LcdRS = REG_IR ; // select the instruction register (IR) 20 LcdRW = CMD_READ ; // Read operation 21 LcdE = ENABLE ; // LCD enable 22 while( (LcdData & 0x80) != 0 ) ; // BF=1 means busy, keep checking 23 24 // LCD not busy 25 26 LcdE = DISABLE ; } 27 // write data to the LCD instruction register (IR) or data register (DR) 28 // input parameter regflag = 1 to the data register (DR), or =0 to the instruction register (IR) 29 // wdata = data written 30 void WriteToLCD(char regflag, char wdata) 31 { 32 CheckLCDBusy() ; // check and wait until the LCD is not busy 33 LcdRS = regflag ; // select register IR/DR 34 LcdRW = CMD_WRITE ; // write operation 35 LcdDataCtrl = 0 ; // set DATA as output type 36 LcdData = wdata ; // place data to DATA 37 LcdE = ENABLE ; // enable the LCD 38 Rev. 1.30 LcdE = DISABLE ; // no delay required, high → low writing completed. 39 } 40 // main function 41 void main(void) 42 { 43 LcdPortCtrl = 0 ; // 3 LCD control pins setup as output types 44 WriteToLCD(REG_IR, 0x38) ; // setup function, 8-bit control, double line display, 5x7 dot matrix 45 WriteToLCD(REG_IR, 0x0f) ; // ON/OFF control, display all data and cursors, cursor blinking 63 March 15, 2011 Using Enhanced Holtek C 46 WriteToLCD(REG_IR, 0x06) ; // entry mode, after the external read and write operation, the address counter AC 47 // automatically adds 1, cursor shifts to the right 48 WriteToLCD(REG_IR, 0x01) ; // clear the display, set the address counter to 0, I/D=1 49 WriteToLCD(REG_IR, 0x80) ; set DD RAM address, set the address counter to 0 50 // position cursor at the first column - first position 51 // start to write characters to LCD DD RAM for LCD displaying 52 for( k = 0, char1 = `A' ; k < 16 ; k++ ) // 16 characters total for one column starting with ` A ' 53 { 54 WriteToLCD(REG_DR, char1);//display character in char1 starting with`A' 55 char1++ ; // next letter 56 } 57 WriteToLCD(REG_IR, 0xC0) ; // DD RAM address setup, set the address counter to 0x40 58 // position cursor at the second column - first position 59 for( k = 0,char1=`a'; k<16 ;k++ )//16 characters in all for one column starting with`a' 60 { WriteToLCD(REG_DR, char1) ;//display characters in char1 starting with`a' 61 62 char1++ ; //next letter 63 } 64 65 WriteToLCD(REG_IR, 0x08) ; // ON/OFF control, no LCD display } 6.6.7 Program Description 2~3 Define Port B as data I/O port, connected to the HD44780 pins 4~7 Define Port C as control signal lines, connected to the HD44780 control pins 8~13 Define control instructions constants for program readability 16~27 Function CheckLCDBusy checks if the LCM is busy and wait until it is idle and then return 18 Set DATA(D0~D7) as inputs 19 Select the instruction register (IR) 20 Select Read operation 21 Enable the LCM 22 Loop: Read data form the LCM and check if the BF bit is 1 (busy.) If yes, keep reading and checking until the LCM is no longer busy. A more rigorous method would be to setup a time limit or a limit on the number of times checked. If the LCM remains busy after this error condition occurs, then an error code should be generated. 25 Disable the LCM 30~39 Function WriteToLCD will write data wdata to the instruction register (IR, regflag=0) or the data register (DR, regflag=1) 32 check and wait until the LCD is not busy 33 Select the register to be IR or DR 34 Write operation 35 Set DATA port as output 36 Place data into DATA (D0~D7) 37 Enable the LCD, pull pin E high 38 Enable the LCD, pull pin E low. No delay required, high low writing completed 41~65 Main function 43 Rev. 1.30 Setup the three LCD control pins as outputs 64 March 15, 2011 Using Enhanced Holtek C 44 Rev. 1.30 Setup LCD functions → 8-bit control, double line display, 5x7 dot matrix 45 Setup LCD ON/OFF control → Display all data and cursor with cursor blinking 46 Set the LCD entry mode → after an external data read/write, the address count AC will be automatically incremented by 1, and the cursor shifts to the right. 48 Clear the display, set the address counter to 0, I/D=1 49 DD RAM address setup → set the address counter to 0 and position the cursor at the first column at the first position 52 Start to write characters to the LCD DD RAM for the LCD display, total of 16 characters for one column starting with `A' 54 Write characters to the LCD RAM for display 55 Next character 57 Set DD RAM address to 0x40, which positions the cursor at the second column at the first position 59 Display the 16 characters starting with `a' at the second column 61 Write the characters to be displayed to the LCD RAM 62 Next character 64 LCD ON/OFF control, no LCD display 65 March 15, 2011 Using Enhanced Holtek C 6.7 HT46R63 MCU with LCD Driver Application Program This chapter uses the HT46R63 to show how to control an LCD driver using C language to display figures on an LCD panel. The LCD simulator in the HT-IDE3000 can be used to simulate the LCD panel without needing actual hardware implementation. Refer to the LCD Simulator chapter in the HT-IDE3000 user's guide for information. 6.7.1 Objective The objective here is to control an LCD panel using the HT46R63 with a C program so as display digits on an LCD panel. The displayed digits are the timing values of an 8-bit clock with seconds as the unit. 6.7.2 Peripheral Components The data controlling whether each LCD pixel, COMx/SEGy, is to be displayed or not is stored in the LCD memory of the HT46R63. This memory is the data memory located in Bank 1 of the MCU, with an addresses range of 40H to 53H. The relationship between the SEGMENTs, COMMONs and addresses is shown in the figure below. Display Memory When a 1 is written to the address bit, the related pixel SEGx/COMy will be displayed. During the application program development and debug process, it is more practical to use the LCD simulator provided in the HT-IDE3000 rather than use actual LCD hardware. Before program development, first configure the LCD panel structure. The example here uses lcddemo.lcd as the panel structure file, which is directly opened by the HT-LCDS software, to give a picture as shown below. Rev. 1.30 66 March 15, 2011 Using Enhanced Holtek C SEG0 SEG1 SEG2 SEG3 SEG4 SEG5 SEG6 SEG7 SEG8 40H 41H 42H 43H 44H 45H 46H 47H 48H COM0 1e 1f — 2e 2f — 3e 3f — COM1 1d 1c — 2d 2c — 3d 3c — COM2 1b 1a 1g 2b 2a 2g 3b 3a 3g The upper part of the figure is the LCD panel figure. The 0 to 9 digits are displayed using the control data written to the LCD RAM by the program. The digits are composed of up to seven segments, each of which is controlled by the associated SEG/COM pixels. Figures on the panel are acquired from the bmp format file which must be assigned while setting the LCD structure. The table below the figure shows the relationship definitions between SEG/COM and each seven digit display segment. For example, the address 40H, 41H, 42H controls the first seven segment figure display and has three commons. in this example so every byte can have only three bits effective at the most. In the example, a number of delays will be will be accumulated and then the numerals displayed on the panel. The ones digit is displayed on the third segment (3a~3g), the tens digit on the second segment (2a~2g) and the hundreds digit on the first segment (1a~1g.) 6.7.3 Circuit Diagram HT46R63 MCU 6.7.4 MCU Configuration Option The HT46R63 configuration options are setup as follows. Options not mentioned can be setup as either enable or disable. LCD duty: 3 COM LCD segment: 20 segments SEG7-SEG10: LCD output SEG11-SEG14: Logical output SEG15-SEG18: Logical output 6.7.5 Program Flow Start Clear LCD RAM (extinguish LCD) Loop, from 0 to 999 Display the digits in tens, hundreds, and ones on the LCD End Rev. 1.30 67 March 15, 2011 Using Enhanced Holtek C 6.7.6 Original Program 1 #include“ht46r63.h" 2 // define the LCD RAM data of the digit`0'~`9', seven bits controls each segment display 3 // each segment pattern - gacf bdc 4 char digit[10] = { 0b01111101, 0b00011000, 0b01110011, 0b01111010, //`0',`1',`2',`3' 5 0b00011110, 0b01101110, 0b01101111, 0b00111000, // `4',`5',`6',`7' 6 0b01111111, 0b01111110 } ; // `8', `9' 7 char LcdRam[20] @ 0x140 ; // LCD RAM 8 void DelayTime(unsigned int count) // 10 * count + 11, if count > 256 9 { 10 while( count != 0 ) count-- ; 11 } 12 // addr (IN) = LCD RAM address = 0x140+x 13 //data1 = data written to the LCD RAM, write addr to bit0~2 and addr+1 to bit3~5 14 // write bit2 of addr+2 to bit 6 15 void DisplayLcd(unsigned char addr, unsigned char data1) 16 { 17 LcdRam[addr - 0x140] = data1 & 0x7 ; // obtain bit 0 ~ 2 18 LcdRam[addr - 0x140+1] = (data1 >> 3) & 0x7 ; // obtain bit 3~5 LcdRam[addr–0x140+2]=((data>>6) & 0x7)<<2;//obtain bit 6, shift 2 bits to the left 19 20 } 21 // main function 22 void main(void) 23 { 24 int k, count ; 25 26 for( k=0 ; k<20; k++) LcdRam[k] =0;// clear the LCD memory to 0, no LCD display 27 for( count=0 ; count < 1000 ; count++ ) // display digits from 0 to 999 28 { 29 k = (count /100) % 10 ; // obtain the hundreds digit 30 DisplayLcd(0x146, digit[k]) ; 31 k = (count / 10) % 10 ; // obtain the tens digit 32 DisplayLcd(0x143, digit[k]) ; // display digit 33 k = count % 10 ; // obtain the ones digit 34 DisplayLcd(0x140, digit[k]) ; 35 _delay(250000) ; // delay one second 36 _delay(250000) ; 37 _delay(250000); 38 _delay(250000) ; 39 40 Rev. 1.30 } } 68 March 15, 2011 Using Enhanced Holtek C 6.7.7 Program Description 4~6 Define the 0 to 9 digits, the LCD RAM control codes and the,display digits 7 Define the address of the array LcdRam[20] to be at 0x40 in RAM bank 1 which is the start address of the LCD RAM. The control code to be displayed must be written to the related address. The LcdRam[0] address is 0x140, LcdRam[1] is 0x141, …, and LcdRam[19] is 0x153. 15~20 Function DisplayLcd writes each part of the control code data1 to the LCD RAM address addr, addr+1, addr+2. Here bits 0~2 of data1 is written to addr bits 0~2, bits 3~5 of data1 is written to addr+1 and bits 0~2, bit 6 of datal is written to addr+2 bit 2. Once the control codes are written to the LCD RAM, the related pixels will immediately respond to the written data. 22~38 Main function 26 Clear the LCD memory to 0, extinguish LCD display 27~37 Loop, display digits on the LCD panel starting from 0 to 999 29 Obtain the hundreds digit 30 Obtain the hundreds digit control code the from array digit[]. Transmit the LCD RAM address 0x146 and control codes to the function DisplayLcd to display the hundreds digit. 31 Obtain the tens digit 32 Obtain the tens digit control code from array digit[]. Transmit the LCD RAM address 0x143 and control codes to the function DisplayLcd to display the tens digit. 33 Obtain the ones digit 34 Obtain the ones digit control code from array digit[]. Transmit the LCD RAM address 0x140 and control codes to the function DisplayLcd to display the ones digit. 35~38 _delay delay for 1 second 6.8 Display General Functions – HD44780 LCM This chapter sums up the functions in the example 6.6 using an easier format and interface for use by other application programs that use an LCD. 6.8.1 initialLCD( ) Define the constants for which are used to initialize the LCD features. Features not defined as follows are with a fixed value which need not modify (default=0). #define LCD_MODE_RIGHT 0x2 // entry mode: I/D=1 cursor shifts to the right #define LCD_MODE_MOVE 0x1 // entry mode: S=1 the display shifts accordingly #define LCD_FUNC_5X10 0x04 // F=1 5x10 dot matrix #define LCD_FUNC_2LINE 0x08 // N=1 dual line display #define LCD_FUNC_8 0x10 // DL=1 8-bit control (DB7~DB0) #define LCD_DISP_B 0x01 // ON/OFF control : B=1 cursor flashing #define LCD_DISP_C 0x02 // C=1 display cursor #define LCD_DISP_D 0x04 // D=1 display all data // function input parameters and definitions // funcset : functionset instruction, bits 4,3,2 = DL, N, F // DL=1 is 4 bit (DB7~DB4) control mode // =0 is 8 bit (DB7~DB0) control mode // N=0 single line display // =1 dual line display // F=0 5x7 dot matrix character // =1 5x10 dot matrix character // dispon : Display ON/OFF instruction, bits 2, 1, 0 = D, C, B // D=0 no data displayed // =1 display all data Rev. 1.30 69 March 15, 2011 Using Enhanced Holtek C // C=0 cursor not displayed // =1 display cursor // B=0 cursor not flashing // =1 cursor flashing // entrymode: enter mode instruction bit 1, 0 = I/D, S // I/D=0 when data is written to or read from the DD RAM, the address counter (AC) will // decrease by 1 so the cursor will shift to the left // =1 when data is written to or read from the DD RAM, the address counter (AC) will // increment by 1 so the cursor will shift to the right // S =1 when data is written to the DD RAM, all display will shift to the left (if I/D=0) or to the right // (if I/D=1) while reading data from the DD RAM the display will not move // =0 display does not move void InitialLCD(char funcset, char dispon, char entrymode) { char bcode ; LcdPortCtrl = 0 ; // three LCD control pins setup as outputs bcode = (funcset & 0x1C) | 0x20 ; // take the funcset bits of 2, 3, 4 (functionset instruction) WriteTo LCD(REG_IR, bcode) ; // function setup, 4/8 bits, single/dual line display, 5x7/5x10 dot matrix bcode = (dispon & 0x7) | 0x08 ; // ON/OFF control, the dispon bits or 0, 1, 2, is the ON/OFF 0, 1, 2 WriteToLCD(REG_IR, bcode) ; // ON/OFF control, display all data and cursors, cursor flashing bcode = (entrymode & 0x03) | 0x04 ; // entry mode, entrymode bit 1, 0 WriteToLCD(REG_IR, bcode) ; // entry mode, after the external data read/write, the address counter (AC) will change WriteToLCD(REG_IR, 0x01) ; // clear display, set the address counter to 0, I/D=1 WriteToLCD(REG_IR, 0x80) ; // DD RAM address setting, set the AC to 0 // set the cursor to the first line at the first position } 6.8.2 PutChar( ) Write the first letter to the LCD DD RAM for display. void PutChar(char bdata) { WriteToLCD(REG_DR, bdata) ; // write to LCD DD RAM } 6.8.3 MoveAt( ) Move the cursor to the indicated location on the LCD, namely change the address counter (AC) to a new address where x and y indicate the xth position on the yth column (x=0~19, y=0~3) . There are a maximum of 20 characters for each column and a maximum of four columns.. void MoveAt(char x, char y) { char bdata ; bdata = x + ((y & 2)==0 ? 0 : 20) + ((y&1)==0 ? 0 : 0x40) + 0x80 ; WriteToLCD(REG_IR, bdata) ; } 6.8.4 main( ) void main(void) { char funcset, disp, entrymode ; Rev. 1.30 70 March 15, 2011 Using Enhanced Holtek C } funcset = LCD_FUNC_2LINE | LCD_FUNC_8 ; // dual line display, 8-bit transmission entrymode = LCD_MODE_RIGHT ; disp = LCD_DISP_B | LCD_DISP_C | LCD_DISP_D ; InitialLCD(funcset, disp, entrymode) ; // initialize LCD 6.9 Key Scan Program 6.9.1 Objective This example introduces how to control a 4x4 keyboard array using I/O lines. Each key is given a specific hexadecimal value. When a key is pushed, the LCD will display the given value of the key in binary format. If multiple keys are pushed at the same time, only the first scanned key value will be displayed. In the C language, the switch/case statement format will replace multiple if/else if format so as to increase program readability. 6.9.2 Peripheral Components Sixteen switches are connected to port A, PA0~PA7, and four LEDs are connected to port B, PB0~PB3. Set PA0~PA3 as outputs and PA4~PA7 as inputs. When a pushed key is scanned by the program, the key value will be displayed using the LEDs connected to Port PB. Scanning is executed from the first column and the first row. Begin by finding the row where the pushed key is located, then find the column. Then transmit the scan code using PA0~PA3 with a 0 for the target row and a 1 for other rows. For example, first search on the first row by sending out a 0111 pattern on PA0~PA3 and read back from PA4~PA. If the value is not 1111, then one of the keys on this row has been pressed. If a 0 appears on PA5, then this means that one of the keys on this column has been pressed. Therefore in this case the pressed key is located on the first row and the second column. 6.9.3 Circuit Diagram Rev. 1.30 71 March 15, 2011 Using Enhanced Holtek C 6.9.4 MCU Configuration Options The configuration option settings of the HT48R10A-1 are as follows; WDT clock source : disable OSC : Ext. Crystal Pull-high PA : Pull-high Pull-high PB : Pull-high Input type PA : Schmitt Trigger BZ/BZ : Disable fSYS = 4M 6.9.5 Program Flow Start Set PB, PA0~PA3 as outputs Set PA4~PA7 as inputs Loop: output scan code for each row from the first to the fourth row Past the forth row Yes No Read scan code from port A No Key pressed on one row Yes Output to port B, turn on the LEDs related to the key Rev. 1.30 72 March 15, 2011 Using Enhanced Holtek C 6.9.6 Original Program 1 #include “ht48r10a-1.h" 2 char scan_code[4] = { 0xfe, 0xfd, 0xfb, 0xf7 } ; 3 void main(void) 4 { 5 char key_code, pos ; 6 int row ; 7 _pbc = 0 ; // set port B as outputs for LED driving 8 _pac = 0x0f ; // set PA0~PA3 as outputs, PA4~PA7 as inputs 9 _pa = _pb = 0 ; // clear PA, PB 10 scan_l: 11 for( row = 0 ; row < 4 ; row++ ) // scan from the first row 12 { _pa = scan_code[row] ; // output the scan code of this row 13 14 key_code = _pa ; // read scan code from PA 15 if( (key_code ^ scan_code[row]) !=0)//a key has been pressed on this row 16 { 17 switch (key_code & 0xf0)//obtain the highest 4 bits (PA7~PA4) 18 { 19 case 0xe0 : pos = row * 4 ; // PA4=0 20 break ; 21 case 0xd0 : pos = row * 4 + 1 ; // PA5=0 22 break ; case 0xb0 : pos = row * 4 + 2 ; // PA6=0 23 24 break ; 25 case 0x70 : pos = row * 4 + 3 ; // PA7=0 26 break ; 27 default : // error goto scan_l;//using other error display is acceptable 28 29 } 30 pos ^= 0x0f ; //set the lowest four bits low to turn on the LEDs 31 _pb = pos ; // turn on the LED 32 break ; // jump out of the loop 33 } 34 } 35 // no key is pressed or LED display has ended 36 37 goto scan_l ; // scan from the beginning } 6.9.7 Program Description Rev. 1.30 2 Define four scan code array scan_code[4] for key scanning 7 Set port B as outputs to drive the LEDs 8 Set PA0~PA3 as outputs, PA4~PA7 as inputs 9 Clear PA, PB 10 Start point for repeat execution 11 Loop, scan from the first row, in total four rows 13 Output the scan code of this row to port A 14 Read scan code from port A 73 March 15, 2011 Using Enhanced Holtek C 15 Check if any key is pressed on this row 17 Key is pressed - check column location // obtain highest 4 bits (PA7~PA4) 19 First column pressed - location is counted to be column x 4 20 Jump out of switch 21 Second column pressed - location is counted to be column x 4 + 1 22 Jump out of switch 23 Third column pressed - location is counted to be column x 4 + 2 24 Jump out of switch 25 Fourth column pressed - location is counted to be column x 4 + 3 26 Jump out of switch 27~28 For other situations, these steps should not be executed under normal conditions. To avoid abnormal conditions from happening, this can be a precaution to set with an incorrect value (-1). Rev. 1.30 30 Set the lowest four bits low to turn on the LEDs 31 Output to Port B, turn on the LED 32 Jump out of the loop, go to the 36th column 36 No key is pressed or has LED display completed once. // scan from the beginning 74 March 15, 2011 Using Enhanced Holtek C Chapter 7 Application Example – Interrupt Program This chapter introduces how to manage microcontroller interrupt events using C. The interrupt service routine in chapter 4.7, describes how to write an interrupt service routine and also includes some special notes. The following rules should be obeyed when defining an interrupt service routine. → The return value type of the function must be a void interrupt → No arguments are allowed in the function → Set the interrupt vector in the interrupt service routine using @ to designate an interrupt vector → It is recommended not to call interrupt service r5outines from other program locations → Do not enable the interrupt to be generated again in the interrupt service routine. Same interrupts should be prevented from being generated. void interrupt ISR_Timer(void) @ 0x08 { } // Define: return value type, no arguments, set the interrupt vector to 0x08 The C compiler will save the register contents after entering the interrupt service routine according to the interrupt requirements. After the interrupt service routine has been executed, the stored contents will be recovered back to their original registers and finally the program will jump back to the point where the interrupted occurred. The interrupt function will also be re-enabled to allow the generation of further interrupts. It is also not necessary to store the contents of some registers in an interrupt service routine, such as BP, MP0, and MP1 etc. to reduce program code size. For a description and usage about #prgama refer to section 4.9. Do not enable the same interrupt function in the interrupt service routine to prevent a double entry to the same interrupt vector from occurring. However other interrupts can be enabled as long as they do not call the same functions which would create data corruption. 7.1 LED Control using the Timer 7.1.1 Objective This example uses the timer to control the on/off time of an LED. The same on and off time is controlled using an interrupt service routine written by C. Rev. 1.30 75 March 15, 2011 Using Enhanced Holtek C 7.1.2 Peripheral Components A single LED is connected to Port A in the HT48R10A-1. Control of this pin requires using the MCU registers INTC, TMRC, and TMR. Each definition is listed in the accompanying table. The TMRC (Timer Control Register) controls the timer functions as well as its start/stop operation. Bit No. Label Function 0~2 PSC0~ PSC2 To define the prescaler stages, PSC2, PSC1, PSC0= 000: fINT=fSYS/2 or fRTC/2 001: fINT=fSYS/4 or fRTC/4 010: fINT=fSYS/8 or fRTC/8 011: fINT=fSYS/16 or fRTC/16 100: fINT=fSYS/32 or fRTC/32 101: fINT=fSYS/64 or fRTC/64 110: fINT=fSYS/128 or fRTC/128 111: fINT=fSYS/256 or fRTC/256 3 TE To define the TMR active edge of timer/event counter (0=active on low to high; 1=active on high to low) 4 TON 5 — 6 7 TM0 TM1 To enable/disable timer 0 counting (0=disabled; 1=enabled) Unused bit, read as "0" To define the operating mode 01=Event count mode (external clock) 10=Timer mode (internal clock) 11=Pulse width measurement mode 00=Unused TMRC(0EH) register Bit No. Label 0 EMI Controls the master (global) interrupt (1= enabled; 0= disabled) Function 1 EEI Controls the external interrupt (1= enabled; 0= disabled) 2 ETI Controls the timer/event counter interrupt (1= enabled; 0= disabled) 3 — Unused bit, read as "0" 4 EIF External interrupt request flag (1= active; 0= inactive) 5 TF Internal timer/event counter request flag (1= active; 0= inactive) 6 — Unused bit, read as "0" 7 — Unused bit, read as "0" INTC (0BH) register TMR is the register which stores the initial value of the timer. The value will also be stored in the preload register when writing to TMR. When the timer is stopped, the value will be written to the timer/counter. Whenever the timer is enabled, the timer counter value will be incremented until a value of FFH is reached, at which point the timer will overflow. At this time, the following situation will occur. → Generate an interrupt signal → Reload the value in the preload register to the timer/counter and keep counting up. If a timer overflow is required at specific counts, then a value of 256 – count should be written to the TMR register. Further range increase can be achieved by using prescaler bits PSC0~PC2 in the TMRC register. If a timer interrupt is required, it is necessary to enable the timer interrupt function in the INTC register by setting the ETI bit to 1. When a timer overflow occurs, a timer interrupt will be generated which will cause the program to jump to the interrupt vector 08H. The interrupt function will control the LED through PA0 pin on Port A. Rev. 1.30 76 March 15, 2011 Using Enhanced Holtek C 7.1.3 Circuit Diagram Connect the PA0 of port A to the negative pole of the LED. 7.1.4 MCU Configuration Option HT48R10A-1 configuration option settings: WDT clock source : disable OSC : Ext. Crystal Pull-high PA : Pull-high Input type PA : Schmitt Trigger BZ/BZ : Disable fSYS = 4M 7.1.5 Program Flow Timer Interrupt Function Start ms variable plus 1 Set PA0 as outputs Set EMI, ETI of INTC Set TMRC as fSYS/256 Select timer mode Smaller or equal to 62 ms variable Larger than 62 Set TON, enable timer Set the ms variable to 0 The interrupt function handles the LED on/off Enter endless loop; allow the timer to control the LED on/off Rev. 1.30 77 return March 15, 2011 Using Enhanced Holtek C 7.1.6 Original Program 1 #include “ht48r10a-1.h" 2 #define _ton _0e_04 // register TMRC bit4, TON 3 char sec_count ; // record second 4 void interrupt ISR_TIMER(void) @ 0x8 // define the interrupt service routine ISR to be located at address 0x08 5 { 6 sec_count++ ; // every 16ms plus 1 7 if(sec_count > 62 ) // exceed 16x62 = 992 ms, one second 8 { 9 sec_count = 0 ; // restart 10 _pa0 ^= 0x01 ; // toggle LED, every second, on/off in sequence 11 } 12 } 13 void main(void) 14 { 15 _pac = 0x01; // set PA0 as output 16 _pa = 0xff ; // turn off LED 17 _intc = 0x05 ; // set EMI, ETI which enables the interrupt function 18 _tmrc = 0x87 ; // set the timer register, fint=fSYS/256, timer mode 19 _tmr = (256 – 250) ; // every 250 clock cycles generates a timer interrupt 20 sec_count = 0 ; // set the initial value 21 _ton = 1 ; // enable the timer and start counting 22 while(1) ; // endless loop 23 } 7.1.7 Program Description 2 Define variable_ton as bit4 of the timer controller TMRC. TON controls the timer start/ stop operation. 3 Define variable sec_count to record seconds. 4~12 Define the timer interrupt service routine ISR_TIMER(void) to be located at address 0x08 6 Add 1 every 16 ms 7 Check if time 62 times ( over 16x62 = 992 ms - one second) 9 Reset sec_count to 0, restart counting 10 toggle LED on and off in sequence every second 12~23 Main program main(void) Rev. 1.30 15 Set PA0 as output connect PA0 to LED 16 Set PA0 to 1 to turn off LED 17 Set EMI and ETI in the interrupt control register (INTC) to enable the interrupt 18 Setup the timer control register (TMRC), fint=fSYS/256. timer mode, fSYS= 4MHz, 1 clock=64us, timer frequency = 4M/256, clock = 1/timer frequency = 64us 19 Set the timer to generate a timer interrupt every 250 clock cycles, 250x64us = 16ms 20 Setup initial value 21 Set_ton=1 to enable the timer count 22 Endless loop while(1) 78 March 15, 2011 Using Enhanced Holtek C 7.2 Analog to Digital Converter Application 7.2.1 Objective The example here uses the analog to digital converter in the HT46R63 MCU to convert external analog signals to digital values and display the value on an LCD panel. The ADC interrupt service routine (ISR) and LCD display programs are written in C. 7.2.2 Peripheral Components The LCD Simulator panel file LcdDemo.lcd and segment patterns provided in the Holtek HTIDE3000 can be used to simulate an LCD for convenience. Refer to the section 6.7. The A/D Converter circuit in the HT46R63 includes the following registers which should be setup before use. → ADR Register (22H) Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 D7 D6 D5 D4 D3 D2 D1 D0 The converted A/D Converter value will be stored in the ADR register, with a value from 0 ~ 255. → ACSR Register (23H) determines the A/D conversion speed Bit No. Rev. 1.30 Label Functions 0 1 ADCS0 ADCS1 ADCS1, ADCS0: select the A/D converter clock source 0,0 : fSYS/2 0,1 : fSYS/8 1,0 : fSYS/32 1,1 : unsdefined 2 CMPL Comparator control (this bit is 0 druing reset) 0 : disable 1 : enable 3-6 — 7 TEST Not used. Read as“0" For testing only 79 March 15, 2011 Using Enhanced Holtek C → ADCR Register (22H) selects the A/D conversion channel which is located on Port B. . Bit No. Label Functions ACS0 ACS1 ACS2 ACS2, ACS1, ACS0 - selects the A/D input channel 0,0,0 : AN0 0,0,1 : AN1 0,1,0 : AN2 0,1,1 : AN3 1,0,0 : AN4 1,0,1 : AN5 1,1,0 : AN6 1,1,1 : AN7 PCR0 PCR1 PCR2 PCR2, PCR1, PCR0 : Set the PB7~PB0 functions 0,0,0 : PB7,PB6,PB5,PB4,PB3,PB2,PB1,PB0 0,0,1 : PB7,PB6,PB5,PB4,PB3,PB2,PB1,AN0 0,1,0 : PB7,PB6,PB5,PB4,PB3,PB2,AN1,AN0 0,1,1 : PB7,PB6,PB5,PB4,PB3,AN2,AN1,AN0 1,0,0 : PB7,PB6,PB5,PB4,AN3,AN2,AN1,AN0 1,0,1 : PB7,PB6,PB5,AN4,AN3,AN2,AN1,AN0 1,1,0 : PB7,PB6,AN5,AN4,AN3,AN2,AN1,AN0 1,1,1 : AN7,AN6,AN5,AN4,AN3,AN2,AN1,AN0 6 EOCB A/D conversion point (=0 means conversion completed) Whenever bits 3~5 setup are changed, a START signal should be transmitted to initialize the A/D converter, or the flag EOCB will be in an uncertain condition. 7 START Enable the A/D converter (0->1->0 => enable, 0->1 => reset A/D converter and set EOCB as 1. 0 1 2 3 4 5 Before starting an A/D conversion, first select the conversion channel on Port B, select the clock speed and then set the START bit which is bit7, in the ADCR register to 0 -> 1 -> 0, to initiate an A/D conversion cycle. After the conversion is finished, the EOCB bit which is bit6 in the ADCR register will be cleared to 0. The interrupt function in the A/D converter should be enabled to generate an interrupt. If the conversion channel or Port B configuration is changed, the A/D converter should be initialised using the following steps: Set the START bit in the ADCR register to 1 and then clear it to 0 within 10 instruction cycles before changing Port B. In order to using the interrupt function in the HT46R62 A/D converter, the interrupt vector and interrupt control configuration are also necessary. → INTC0 Register Bit No. Label Function 0 EMI Control the master (global) interrupt (1=enabled; 0=disabled) 1 EEI0 Control the external interrupt 0 (1=enabled; 0=disabled) 2 EEI1 Control the external interrupt 1 (1=enabled; 0=disabled) 3 ETI Control the Timer/Event Counter interrupt (1=enabled; 0=disabled) 4 EIF0 External interrupt 0 request flag (1=active; 0=inactive) 5 EIF1 External interrupt 1 request flag (1=active; 0=inactive) 6 TF Internal Timer/Event Counter request flag (1=active; 0=inactive) 7 — For test mode used only. Must be written as "0"; otherwise may result in unpredictable operation. INTC0 (0BH) Register The EMI bit, bit0, should be set to 1 to enable the master interrupt enable. Rev. 1.30 80 March 15, 2011 Using Enhanced Holtek C →→ INTC1 Register Bit No. Label Function 0 ETBI Controls the time base interrupt (1=enabled; 0=disabled) 1 EADI Control the A/D converter interrupt (1=enabled; 0=disabled) 2 ERTI 3 — Control the real time clock interrupt (1=enabled; 0=disabled) Unused bit, read as "0" 4 TBF Time base time-out interrupt 0 request flag (1=active; 0=inactive) 5 ADF End of A/D conversion interrupt request flag (1=active; 0=inactive) 6 RTF RTC time-out interrupt request flag (1=active; 0=inactive) 7 — Unused bit, read as "0" INTC1 (1EH) Register The bit1 should be set to 1 to enable the A/D converter interrupt function. 7.2.3 Circuit Diagram HT46R63/HT46C63 Rev. 1.30 81 March 15, 2011 Using Enhanced Holtek C 7.2.4 MCU Configuration Options HT46R63 configuration option setup: WDT clock source : disable OSC : Ext. Crystal fSYS= 4M LCD duty: 3 COM LCD segment: 20 segments SEG7-SEG10: LCD output SEG11-SEG14: Logical output SEG15-SEG18: Logical output Comparator : disable 7.2.5 Program Flow Start Clear LCD RAM to 0, no display Enable ADC interrupt and master interrupt function Setup the ADC clock, conversion channel, conversion channel to enable the A/D converter A/D conversion completed? Not completed Completed Display the converted value on the LCD panel 7.2.6 Original Program 1 #include “ht46r63.h” 2 // Definitions in the header file are variable_emi = _intc0.0, _eadi = 1e1(INTC1 bit1), 3 // A/D converter registers are _adr (0x21), _adcr (0x22), acsr (0x23) 4 #define adc_start_22_7 // bit 7 (SATRT) of ADCR register 5 #define fSYS8 0x01 // ADC clock source and fSYS/8 6 #define CH_AN0 0 // ADC conversion channel 7 #define ADC_2CH 2 // ADC master channel number: 2, AN0 (PB0), AN1 (PB1) 8 char intflag = 0 ; // ADC interrupt flag 9 unsigned char adcvalue = 0 ; // ADC value after conversion 10 void interrupt ADC_ISR(void)@0x14 // ADC interrupt service routine - interrupt vector is 0x14 11 { 12 Rev. 1.30 intflag = 1 ; // set the ADC interrupt flag 82 March 15, 2011 Using Enhanced Holtek C 13 adcvalue = _adr ; // read the converted value 14 } 15 // Enable the ADC conversion 16 // adc_clk = clock source of ADC, fSYS/2, fSYS/8, fSYS/32 (bit0~1 of ACSR) 17 // channel = channel number of AN, 0 ~ 7 (bit 0~2 of ADCR) 18 // port_cfg = port B setting (bit 3~5 of ADCR) 19 void StartADCTrans(char adc_clk, unsigned char channel, unsigned char port_cfg) 20 { 21 _acsr = adc_clk ; // set the clock source and frequency 22 adcr = (port_cfg << 3) | channel ; // set the channel and Port B configuration 23 adc_start = 1 ; adc_start = 0;//change the ADC channel and Port B configuration 24 adc_start = 0 ; adc_start = 1 ; adc_start = 0 ; // enable the A/D Converter 25 } 26 // the following function (except for the main) are the same as Chapter 6.7 27 // Define the LCD data for digits `0' ~ `9' with 7 bits controlling each segment display 28 // patterns for each segment gacf bdc 29 char digit[10] = { 0b01111101, 0b00011000, 0b01110011, 0b01111010, //`0',`1',`2',`3' 30 0b00011110, 0b01101110, 0b01101111, 0b00111000, // `4',`5',`6',`7' 31 0b01111111, 0b01111110 } ; // `8', `9' 32 char LcdRam[20] @ 0x140 ; // LCD RAM 33 void DelayTime(unsigned int count) // 10 * count + 11, if count > 256 34 { 35 while( count != 0 ) count-- ; 36 } 37 // addr (IN) = LCD RAM address = 0x140+x 38 // datal = write to the LCD RAM data with bits 0~2 to addr and bits 3~5 to addr+1 39 // write bit 6 to bit 2 in addr+2 40 void DisplayLcd(unsigned char addr, unsigned char data1) 41 { 42 LcdRam[addr – 0x140] = data1 & 0x7 ; // take bit0~2 43 LcdRam[addr – 0x140+1] = (data1 >> 3) & 0x7 ; // take bit3~5 LcdRam[addr – 0x140+2] = ((data >> 6) & 0x7) << 2; take bit 6 and 44 move 2 bits to the left 45 } 46 // main program 47 void main(void) 48 { 49 int k ; 50 51for( k=0 ; k < 20 ; k++) LcdRam[k] = 0 ; // clear the LCD memory to 0, no LCD display Rev. 1.30 52 _emi = 0 ; // stop all interrupt generation 53 _emi = 1 ; // ADC interrupt function enabled 54 _emi = 1 ; // all interrupt functions enabled 55 Intflag = 0 ; // ADC interrupt flag 56 StartADCTrans(fSYS8, CH_AN0, ADC_2CH) ; // start ADC conversion 57 while(1) // endless loop, wait for an ADC interrupt 83 March 15, 2011 Using Enhanced Holtek C 58 { 59 if( intflag == 1 ) break ; // ADC interrupt generated, jump out of the loop 60} 61 // display the converted digits on the LCD panel 62 k = adcvalue /100 ; // obtain the hundreds digit 63 DisplayLcd(0x146, digit[k]) ; // display the hundreds digit 64 k = (adcvalue /10)% 10 ; // obtain the tens digit 65 DisplayLcd(0x143, digit[k]) ; // display the tens digit 66 k = adcvalue % 10 ; // obtain the ones digit 67 DisplayLcd(0x140, digit[k]) ; 68 _delay(250000) ; // delay 1 second 69 _delay(250000) ; 70 _delay(250000) ; 71 _delay(250000) ; 72for( k=0 ; k < 20 ; k++ ) LcdRam[k] = 0 ; // clear the LCD memory to 0, no LCD display 73 } 7.2.7 Program Description 4 Define the START bit, bit 7 in the ADCR register 5~7 Define the ADC clock source, conversion channel and total channels used in this example 8 Define the ADC interrupt flag =1 indicating an ADC conversion complete 9 Define the variable and save the values converted by the ADC 10~14 Define the ADC interrupt service routine ADC_ISR(void) - the interrupt vector is 0x14 12 Set the ADC interrupt flag 13 Read the converted value from the register and save the variable 19~25 Define function StartADCTrans() to execute the ADC conversion 21 Setup the ADC conversion clock and frequency 22 Select the ADC conversion channel and total channels 23 Change Port B configuration 24 Enable the ADC conversion function 26~45 Same as Chapter 6.7, - the LCD display function 47~73 Main function, setup and enable an ADC conversion and display the converted value on the LCD panel 51 Clear the LCD memory to 0, no LCD display 52 Stop all interrupt generation 53 ADC interrupt function enabled 54 All interrupt functions enabled 55 Clear the ADC interrupt flag to 0 56 Enable an ADC conversion 57~60 Endless loop, wait for an ADC interrupt and then jump out of the loop 59 Generate an ADC interrupt, jump out of the loop 62~67 Display the converted value on the LCD panel 62~63 Obtain the hundreds digit and display 64~65 Obtain the tens digit and display 66~67 Obtain the ones digit and display 68~69 Delay display time for 1 second 72 Rev. 1.30 Disable the LCD display 84 March 15, 2011 Using Enhanced Holtek C Chapter 8 NiMH Battery Charger (HA0084T) Application using the HT46R52A 8.1 Objective With the miniaturization trend of electric appliances, portable products have become more and more popular, such as mobile phones, digital cameras, PDA, MP3 players etc. The need for rechargeable batteries has accompanied this trend. Among all kinds of the rechargeable battery, the NiMH battery is especially used to replace the traditional battery having the advantages of high capacity, low price and environmental protection concerns. Holtek provides a NiMH battery charger demo board for the designer to design a NiMH battery charger. ■ Charger Features → Uses fixed current to supply power charging with 70mA for slow charging and 575mA for quick charging → Provides a battery discharge option to reduce memory effects of the battery – uses a 50mA discharge current → Uses -∆V detection to check if the battery is fully charged → Automatically stops charging after a charging time of up to 6 hours → Two LED display to show the charging status → Single battery charging at a time ■ Charger Description The charger uses a fixed current to charge an NiMH battery. It will immediately start charging when a battery is inserted with an option to discharge the NiMH battery before charging. It will enter the charging mode immediately after the discharge. Prior to the charging process, the charger will determine whether the voltage is greater than 1.25V so as to enter the quick charging mode, or else it will enter the slow charging mode to protect the battery then and step into the quick charging mode after the slow charge has finished. ■ Charging Method Implements a NiMH battery charger using the HT46R52A and fixed current source. The HT46R52A provides a PWM output to and an ADC input for current control. The HT46R85A offers a four 4-channel ADC to read external signals for battery voltage detection. When a -∆V in the battery is detected, the battery is fully charged. The 12-bit ADC can provide a resolution up to 1.2mV. ■ Charging Flow Description → When the power is off, the LED on the demo board is off. → When the power is on, the green LED remains on and the others will be on for on second and then off, during which time the demo board is waiting for the insertion of a NiMH battery. → After a NiMH battery is inserted, the battery starts to be charged with two LEDs indicating the charging status. → When the NiMH battery is fully charged, it will stop charging with two LEDs indicating the fully charged status. → When a NiMH battery is fully charged and then removed, two LEDs will indicate that it is waiting for further battery charging → Yellow and red LEDs both off: waiting for battery to be inserted → Yellow LED on and red LED off: quick charging in progress → Yellow and red LEDs both on: the battery is fully charged or has been charged for six hours. → Yellow LED on with red flashing: slow charging in progress Rev. 1.30 85 March 15, 2011 Using Enhanced Holtek C → Red LED on with yellow flashing: discharging in progress The table below lists some reference values used during the charging process Parameters Default Values Functions Charging_time 240: about 6 hours Charging time setup, 1.5 min. per unit, stop charging when time is up V48 V155 1270: about 1.55V Stop charging when battery voltage over 1.55V is detected, 1.22mV per unit V25 V125 1024: about 1.25V Quick charging when battery voltage over 1.25V is detected, or enter slow charging mode, 1.22mV per unit V22 V115 942: about 1.15V Start charging when voltage over 1.15V during discharging, 1.22mV per unit V03 V01 82: about 0.1V Battery voltage must be larger than 0.1V to indicate that a battery has been inserted, 1.22mV per unit. Vpk_time 64: about 64 seconds Battery has exhibited a -rV situation. After a Vpk_time the battery will be considered to be fully charged. 1 sec. per unit V20mv 180H: about 29mV Control the slow charging current to be larger than 58mA, every 10H indicates 2.44mA V30mv 200H: about 39mV Control the slow charging current to be smaller than 78mA, every 10H indicates 2.44mA V250mv E80H: about 283mV Control the quick charging current to be larger than 566mA, every 10H indicates 2.44mA V260mv F00H: about 293mV Control the quick charging current to be smaller than 586mA, every 10H indicates 2.44mA 8.2 Register and Peripheral Components The example here can be used for the PWM (Pulse Width Measurement) to control the current during battery charging. When configuring the MCU structure, bit0 (PD.0) of Port D should be set to be in the PWM mode. PD0 should be setup as an output (using the PDC control register) in the program. Write a 1 to PD0 before activating the PWM output and then write the required value to the PWM register. The table below shows the I/O function of PD0. I/O Mode I/P (Normal) O/P (Normal) I/P (PWM) O/P (PWM) PD0 Logical Input Logical Output Logical Input PWM Parameter AC (0~3) i < AC Modulation cycle i (i=0~3) i ≥ AC DC (Duty Cycle) DC+1 64 DC 64 PWM Modulation Frequency PWM Cycle Frequency PWM Cycle Duty fSYS/64 for (6+2) bits mode fSYS/256 [PWM]/256 The table above describes the duty cycle for each modulation cycle in the PWM (6+2) mode. The ACSR register configures the A/D converter clock frequency. Rev. 1.30 86 March 15, 2011 Using Enhanced Holtek C Bit No. Label Function Selects the A/D converter clock source 00=system clock/2 01=system clock/8 10=system clock/32 11=undefined ADCS0 ADCS1 0 1 2~6 — Unused bit, read as "0" 7 TEST For test mode used only ACSR (23H) Register The ADCR register controls the A/D converter enable/disable and the channel select of the PB input port. Bit No. Label 0 1 2 ACS0 ACS1 ACS2 Function Defines the analog channel select 3 4 5 PCR0 PCR1 PCR2 Defines the port B configuration select. If PCR0, PCR1 and PCR2 are all zero, the ADC circuit is powered off to reduce power consumption. 6 EOCB Indicates end of A/D conversion. (0=end of A/D conversion) Each time bits 3~5 change state the A/D should be initialised by issuing a START signal, otherwise the EOCB flag may have an undefined condition. See "Important note for A/D initialisation". 7 START Starts the A/D conversion. A → 1 → 0=Start 0 → 1=Reset A/D converter and set EOCB to "1" ADCR (22H) Register ACS2 ACS1 ACS0 Analog Channel 0 0 0 AN0 0 0 1 AN1 0 1 0 AN2 0 1 1 AN3 1 0 0 AN4 1 0 1 * 1 1 0 * 1 1 1 * Analog Input Channel Selection PCR2 PCR1 PCR0 4 3 2 1 0 0 0 0 PB4 PB3 PB2 PB1 PB0 0 0 1 PB4 PB3 PB2 PB1 AN0 0 1 0 PB4 PB3 PB2 AN1 AN0 0 1 1 PB4 PB3 AN2 AN1 AN0 1 0 0 PB4 AN3 AN2 AN1 AN0 1 0 1 AN4 AN3 AN2 AN1 AN0 1 1 0 1 1 1 Undefined, cannot be used Port B configuration Rev. 1.30 87 March 15, 2011 Using Enhanced Holtek C Register Bit7 Bit6 Bit5 Bit4 BIt3 Bit2 Bit1 Bit0 ADRL (20H) D3 D2 D1 D0 0 0 0 0 ADRH (21H) D11 D10 D9 D8 D7 D6 D5 D4 Note: D0~D11 is A/D conversion result data bit LSB~MSB. The execution result of the AC converter will be saved in the ADRL and ADRH registers. 8.3 Circuit Diagram 8.4 MCU Configuration Options HT46R52configuration option setup: WDT Disable CLRWDT Two clear instructions WDT clock source WDTOSC Pull-high PD0 Non-Pull-high LVR Disable OSC RC PD0/PWM Enable PWM PA3/PFD Disable PFD ----------------------------------------------------------------------------------76543210 Wake-up PA0- 0 0 0 0 0 0 0 0 1:Wake-up, 0:Non-Wake-up Pull-high PA0- 1 1 1 1 1 1 1 1 1:Pull-high, 0:Non-Pull-high Pull-high PB0- - - - 1 1 1 1 1 1:Pull-high, 0:Non-Pull-high ----------------------------------------------------------------------------------- Rev. 1.30 88 March 15, 2011 Using Enhanced Holtek C 8.5 Program Flow ■ Main Program START Initial process N Detect if the battery is inserted? N Discharge function is selected in the charging mode? N Battery voltage larger than 1.25V? Y Discharge the battery, detect if the voltage is smaller than 1.0V? Y Y N Slow charging N Y Quick charging Check if a –∆V battery voltage condition occurs? N Detect if the charging time is larger than 6 hours? Y Detect if the battery has been removed? Y Battery removed indication N Rev. 1.30 89 March 15, 2011 Using Enhanced Holtek C ■ Program Initialisation Initial process START Set I/O port PWM = 0 Red and yellow LED off Setup ADCR and ACSR Clear RAM STOP Charging Enable Timer Interrupt Set Timer 4ms interrupt once Red, yellow LED display for 1 sec. and turn off RET Rev. 1.30 90 March 15, 2011 Using Enhanced Holtek C ■ Quick Charging Process Program Quick charging START Measure if the current charging current is larger than 565mA? N Increase PWM value Y Decrease PWM value N Measure if the current charging current is smaller than 585mA? Y Charging time longer than 6 hours? Y N Battery voltage larger than 1.55V? Y Charging completed N Quick Charging EXIT Rev. 1.30 91 March 15, 2011 Using Enhanced Holtek C ■ Slow Charging Process Program Slow charging START Measure if the current charging current is larger than 60mA? N Increase PWM value Y Decrease PWM value N Measure if the current charging current is smaller than 80mA? Y Charging time longer than 6 hours? Y N Battery voltage larger than 1.55V? Y Charging completed N Quick Charging EXIT Rev. 1.30 92 March 15, 2011 Using Enhanced Holtek C 8.6 Original Program and Program Description ■ File main.c 1 2 void main() 3 { 4 5 cinitial(); // initial all RAM & IO, turn LED (green) on 6 ch0_old = 0 ; // a new run 7 8 while(1) // infinite loop 9 { 10 while(1) // check battery status : exist/not exist 11 { 12 bat_main_sub(); // get battery's status, the battery is placed or not 13 // set the battery's LED flag 14 if( bat0_on_flag && !ch0_old ) // battery is placed, and it's a new run 15 break ; 16 17 ch0_old = 0 ; // flag : this is new run 18 } 19 20 // bat0_on_flag=1 battery is placed 21 // 1. check discharge switch, PB3 22 if( !_pb3 ) 23 Ch0_Discharging(); // start to discharge 24 25 // ch0_battery_charging_sub_lp: 26 // 2. do battery charging 27 28 Ch0_DoCharging() ; 29 30 } } ■ Program Description 2 Main program 5 Call function cinitial() to clear the RAM, setup I/O status, turn on green LED 6 Set flag ch0_old=0: new cycle execution 8~29 Outside endless loop, program execution never ends in this loop 10~18 Inside loop, detect if the battery is placed in the charger, if yes, jump out of the loop or keep detecting if no 12 Call function bat_main_sub() to detect the battery status 14~15 If a battery is inserted, which is not the previous one, jump out of the loop for correct handling procedure Rev. 1.30 17 If not, set flag and wait for the next battery and continue detecting 22 Execution over which means the battery has been inserted to the waiting charger Check if PB3 of Port B is a low voltage (needs to be discharged first) 93 March 15, 2011 Using Enhanced Holtek C 23 Yes, call function Ch0_Discharging() to execute the discharge function 28 Call function Ch0_DoCharging() to execute the charging function //------------------------------------// Ch0_DoCharging() // 1. battery charging with slow speed // 2. battery charging with fast speed //------------------------------------1 void Ch0_DoCharging() 2{ 3 while(1) 4 { 5 bat0_flag_initial(); // initialize flags, set PWM 6 madc_on_sub(); // check battery's status 7 8 if( !ch0_old ) show_bat0_led(); //show led which flag for battery 9 10 _delay(250000) ; // delay 0.25s 11 _delay(250000) ; 12 _delay(250000) ; 13 _delay(250000) ; // total delay 1s 14 15 if( !bat0_on_flag ) // when have no battery in,jump 16 { 17 led0_on_flag = 0 ; 18 led1_on_flag = 0 ; // clear led show 19 ch0_old = 0 ; // the battery charging finished flag clear 20 21 return ; // jump back } 22 23 if( bat0_voltage_over_flag ) // when battery charging finish,jump 24 { led0_on_flag = 1 ; // charging finished,led0&led1 on 25 26 led1_on_flag = 1 ; // RED, Yellow are lit 27 ch0_old=1; // old battery 28 return ; // check battery status again 29 } 30 31 if( bat0_1c_charging_flag ) // fast charging 32 { 33 Ch0_FastCharging() ; 34 return ; 35 } 36 37 // slow charging mode 38 bat0_flag_initial(); // initial charging. 39 bat0_charging_initial(); // load initial battery's average value 40 Rev. 1.30 94 March 15, 2011 Using Enhanced Holtek C 41 // bat0_01c_charging_ap: 42 while(1) 43 { 44 bat0_50ma_charging(); // charging battery by 50ma current 45 stop_charging(); // stop charging 46 _delay(250000) ; // delay 0.25s 47 _delay(250000) ; // total delay 0.5s 48 49 madc_on_sub(); // check battery status 50 led0_on_flag = 1 ; 51 led1_on_flag ^= 1 ; // toggle flag (Red LED flash) 52 53 ad_4_data(); // load battery's average 54 get_Vbat0_inc80mv(); 55 get_bat0_Vpeak(); 56 57 if(!bat0_on_flag) // if no batttery in 58 { 59 led0_on_flag=0; // battery remove,led0&led1 off 60 led1_on_flag=0; 61 ch0_old=0; // flag for battery remove already 62 return ; // check battery status again 63 } 64 65 if( time_1min >= charging_time ) // battery charging's time over 66 { 67 led0_on_flag=1; //charging finished,led0&led1 on 68 led1_on_flag=1; 69 ch0_old=1; // flag for battery : no change of battery 70 return ; // check battery status again 71 } 72 73 if( bat0_1c_charging_flag ) 74 break ; 75 } 76 77 led0_on_flag=1; 78 79 led1_on_flag=0; } 80 } Rev. 1.30 95 March 15, 2011 Using Enhanced Holtek C ■ Program Description 1 This loop is an endless loop to execute the charging operation and control the LED on or off flags according to post charge status. Finally it returns to the main program. 3 Endless loop 5 Call function bat0_flag_initial() to setup the initial value of the parameter and PWM 6 Call function madc_on_sub() to read the battery voltage from the A/D converter and setup the related flag 8 If the battery has just been inserted, the red LED is used to display the current status 10~13 Delay 1 second with LED flashing The following steps are determined according to the battery voltage. 15~21 If the battery voltage is less than 0.1V which indicates that no battery is inserted, then set the red and yellow LED flags to be off and return 23~29 If the battery voltage exceeds 1.55V which means the battery is charged, set the LED flag and return 31~35 If the battery voltage is between 1.25 and 1.55V which means that charging is in progress, call function Ch0_FastCharging() for quick charging and return after completion 38~79 Use slow charging if the battery voltage is between 0.1V~1.25V 38 bat0_flag_initial() set the LED flag, PWM value 39 bat0_charging_initial() set the initial value for charging 42~75 Loop, execute slow charging 44 bat0_50ma_charging() control the battery charging current for 6 hours using the PWM 45~47 Stop charging and wait for 0.5 second 49 madc_on_sub() checks the voltage status of the charged battery 50~51 Set the LED display flag (red blinking, yellow on) 53 ad_4_data() calculates the average voltage value 54~55 Adjust the voltage value and peak value 57~63 Check again the battery status, if the battery has been removed set the flag and return 65 If the charging time has exceeded 6 hours, set the completed LED flag and return 73 If the required voltage has not yet been reached, it will return to the loop on line 3 for recharging //--------------------------// bat0_1c_charging_ap_lp: // fast charging mode //---------------------------1 void Ch0_FastCharging() 2{ 3 bat0_flag_initial(); // initial charging. 4 bat0_charging_initial(); // load initial battery's average value 5 6 while(1) 7 { 8 bat0_500ma_charging(); // charging battery by 500ma current 9 stop_charging(); // stop charging 10 madc_on_sub(); // check battery status 11 Rev. 1.30 96 March 15, 2011 Using Enhanced Holtek C 12 if( bat0_voltage_over_flag ) // charging has been finished 13{ 14 led0_on_flag=1; // charging finished, Yellow and Red LED on 15 led1_on_flag=1; 16 ch0_old=1; // flag for battery 17 18 return ; // check battery status again } 19 20 // set LED status 21 led0_on_flag=1; // Yellow LED is lit 22 led1_on_flag=0; // Red LED extinguish 23 24 ad_4_data(); // load battery's average 25 get_Vbat0_inc80mv(); 26 get_bat0_Vpeak(); 27 28 if(!bat0_on_flag) // battery is removed 29 { 30 led0_on_flag=0; // battery remove,led0&led1 off 31 led1_on_flag=0; 32 ch0_old=0; // flag for battery remove already 33 return ; // check battery status again 34 } 35 36 if(time_1min >= charging_time || // battery charging's time over 37 38 bat0_Vpeak_charging_ok ) // battery charging ok { 39 led0_on_flag=1; //charging finished,led0&led1 on 40 led1_on_flag=1; 41 ch0_old=1; // flag for battery 42 return ; //check battery status again 43} 44 } //charging go on 45 } ■ Program Description Quick charging 3~4 Set the initial charging value // initial charging 6~44 Endless loop 8 Uses constant current charging - adjust the charge current according to the battery voltage, maximum of 6 hours 9~10 After completion, check the charged battery voltage 12~18 If the battery if fully charged, set the completed LED flag and return 21~22 Set the LED flags to be yellow LED on, red LED off (indicating quick charging status) 24~26 Calculate and save the average voltage value, peak voltage value 28~34 Check if the battery has been removed, set the LED flag to off status and return 36~42 If the charging time has reached 6 hours or the voltage has reached its peak value, set the completed LED flag and return 44 Rev. 1.30 If none of the above status has occurred , return to line 6 for quick charging 97 March 15, 2011 Using Enhanced Holtek C //-------------------------------// timer_isr //-------------------------------1 void interrupt timer_isr(void) @ 0x08 2{ 3 unsigned char tmp ; 4 5 tmp = _pa ^ 0x40 ; // read from PA and XOR PA6 6 _pa = tmp ; // output PA6 7 8 // turn on/off LED 9 if( (tmp & 0x40) != 0 ) // current PA6=1 10{ 11 _pa5 = 1 ; 12 if( led2_on_flag ) _pa5 = 0 ; 13 14 _pa4 = 1 ; // Red LED extinguish 15 if( led1_on_flag ) 16 17 _pa4 = 0 ; // Red LED lit } 18 else // PA6=0 19 { 20 _pa5 = 0 ; 21 if( led3_on_flag ) _pa5 =1 ; 22 23 _pa4 = 0 ; // Yellow LED extinguish 24 if( led0_on_flag ) 25 26 _pa4 = 1 ; // Yellow LED is lit } 27 28 // calculate the time 29 time_4ms++; // count 30 if(time_4ms==250) // 1 second 31{ 32 time_4ms=0; // clear ms 33 time_1s++; // 1s count add 34 Vpeak_cx++; // Vpeak_cx add 35 if(time_1s==90) // 90 seconds 36{ 37 time_1s=0; // time for 90s 38 time_1min++; 39 charging_cx++; // total charging time, increase 1 for every 1.5 minutes 40} 41 } 42 } Rev. 1.30 98 March 15, 2011 Using Enhanced Holtek C ■ Program Description Timer interrupt service routine timer_isr Main function is to control the on/off time of the red and yellow LEDs. The other function is to measure the charging time. 5 Read the current LED control code (PA6) and invert 6 Set the control status after the inversion 9~17 To manage the red LED (PA6 set to a high level) Turn off the red LED first. If the LED flag is to turn on, then turn on the red LED 18~26 To manage the yellow LED (PA6 cleared to a low level) Turn off the yellow LED first. If the LED flag is to turn on one then turn on the yellow LED 28 The following steps will measure the charging time 29 Interrupt generated every 4ms - the number of occurrences is accumulated 30~41 When 1 sec. is reached, continue and measure the number of 1 sec occurrences measure the voltage peak value 35~40 When 90 seconds is reached, continue and measure the number of 90 sec, occurrences and thus the total charging times original program sub.c #define RAM_START 0x28 #define RAM_END 0x7F //------------------------------------// cinitial() // turn LED (green) on for 1 second //------------------------------------1 void cinitial() 2{ 3 char *addr ; 4 5 _pa=0x03; 6 _pac=0x80; // PA7:I/P, others are O/P 7 _pbc=0xff; // PB are In 8 _pb=0x00; 9 _pdc=0x00; // PD are Out 10 _pd=0x00; 11 12 _acsr = 0x02; // AD clock source = system clock / 32 13 _adcr = ch3+a0; // set AD channel number and PB 14 _pwm = 0x00; // pwm output 0 15 16 for( addr=(char *)RAM_START ; addr <= (char *)RAM_END ; addr++ ) 17 *addr = 0 ; // clear RAM 18 Rev. 1.30 19 stop_charging(); // stop PWM 20 _intc = 0x05; // enable Timer/Counter 0 21 _tmr = (256-250); // set tmr’s value for 4ms tmr (250*16us = 4ms) 22 23 _tmrc = timer_mode+timer_on+T1_64; // set TMR0 : timer mode, timer on and // fSYS/64, fSYS=4MHz 24 led0_on_flag=1; // set LED flag 99 March 15, 2011 Using Enhanced Holtek C 25 led1_on_flag=1; 26 led2_on_flag=0; 27 led3_on_flag=0; 28 29 _delay(250000) ; // delay 0.25s 30 _delay(250000) ; // total delay 0.5s for LED on 31 32 led0_on_flag=0; 33 led1_on_flag=0; 34 led2_on_flag=0; 35 led3_on_flag=0; // clear all led flag, turn all LEDs off 36 _delay(250000) ; // delay 0.25s 37 _delay(250000) ; // delay 0.25s 38 } ■ Program Description This function is used to initialise the register and memory space and setup the required structure. 5~10 12 13 Set Port A: PA7 setup as an input and all other pins as outputs, Port B setup as inputs Port D setup as outputs and stop the PWM function Select (fSYS/32) as the A/D converter clock source Select channel0 of the A/D converter and PB0, PB1, PB2 of Port B as the signal input channels 14 PWM output is zero 16~17 Clear the data memory, addresses 28H ~ 7FH cleared to zero 19 Stop charging 20 Enable the timer interrupt function 21 Set the timer interrupt interval to be 4ms (250 times) (250x16us = 4ms) 22 Set the timer to be in the timer mode with a clock frequency of fSYS /16 and start timer counting 24~27 Set the LED status as red and yellow both illuminated 29~30 LED status persists for 0.5 second 32~35 Change the LED status to be red and yellow both extinguished 36~37 LED status persist for 0.5 second //---------------------------// bat_main_sub() // get battery status //---------------------------1 void bat_main_sub() 2{ 3 stop_charging(); // stop PWM 4 madc_on_sub(); // get battery's voltage and check status 5 6 if( !ch0_old ) show_bat0_led(); // show led 7 8 _delay(250000) ; // delay 0.25s 9 _delay(250000) ; 10 _delay(250000) ; 11 _delay(250000) ; // total delay 1s 12 } Rev. 1.30 100 March 15, 2011 Using Enhanced Holtek C ■ Program Description This function detects if there is a battery in the charger waiting to be charged. 3 Stop the PWM output 4 Read from the A/D converter if a battery has been inserted and set the corresponding flag 6 If no battery is being charged or the battery has been removed, show the current status using the LEDs 8~11 LED status remains for 1 second //-------------------------------// bat0_flag_initial() //------------------------------1 void bat0_flag_initial() 2{ 3 Vpeak_cx=0; 4 bat0_Vpeak_charging_ok=0; 5 time_1min=0; 6 _pwm = 0x10 ; 7 time_4ms = 0 ; 8 time_1s = 0 ; 9} ■ Program Description This function configures the battery charging flags and the timer frequency 3 Set the fixed time of the continuous voltage peak time value to be zero. When the charging voltage remains the same within a certain length of time, the flag will be set as fully charged. 4 Set the peak value to be zero. When this flag=1, the battery is fully charged. 5~8 Reset the initial value of the timer and the PWM to zero //--------------------------------// bat0_charging_initial() //--------------------------------1 void bat0_charging_initial() 2{ 3 int i ; 4 5 6 for( i = 0 ; i < 8 ; i++ ) ad_4_data(); 7 8 Vbat0_peak = Vbat0_old = Vbat0 ; 9} ■ Program Description This function configures the voltage value and voltage peak value of the uncharged battery for the purpose of comparison. 5~6 Calculate the average of the previous eight voltage values as a reference 8 Store the reference value //--------------------------------------// madc_on_sub() // get current battery voltage and status 1 void madc_on_sub() Rev. 1.30 101 March 15, 2011 Using Enhanced Holtek C 2{ 3 Vbat0_old = Vbat0 ; // save old battery value 4 GetBatteryVoltage(); // get battery voltage from ADC channel 0 5 check_battery0_status(); //check battery’s status, set/reset flags 6} ■ Program Description The function reads the battery voltage value and configures the corresponding flag. 3 Save the previous voltage value 4 Read the battery voltage value 5 Set the corresponding flag according to the voltage value //---------------------------------------// GetBatteryVoltage() adc_on() // read battery's voltage from channel 0 //---------------------------------------1 void GetBatteryVoltage() 2{ 3 stop_charging(); // stop pwm output 4 _delay(10000) ; // delay 10ms (4MHz) 5 // start to convert by using ADC 6 _adcr = ch3+a0; // enable ADC CH0 7 _emi=0 ; // disable all interrupts 8 _start=0; 9 _start=1; 10 _start=0; // start ADC 11 while(_eoc); // wait for ADC conversion completion 12 _emi=1; // enable interrupt 13 14 // read the conversion result 12 bits, adrl = (D3,D2,D1,D0,0,0,0,0), 15 // adrh=(D11,D10,D9,D8,D7,D6,D5,D4) 16 17 bat0 = (unsigned int)_adrh ; 19 bat0_voltage = bat0 ; 20 } ■ Program Description This function reads and stores the battery voltage value. 3 Temporarily suspend the PWM output 4 Wait for 10ms 6 Enable channel 0 of the A/D converter 7 Disable all interrupt generation 8~10 Enable the A/D converter, and initiate a conversion cycle 11 Wait for the completion of the A/D converter 12 Enable the interrupt 17~19 Read the conversion value, combine as a 16-bit voltage value and store //-----------------------------// a0_discharging() //--------------------------void a0_discharging() // this function is to execute the battery discharging function Rev. 1.30 102 March 15, 2011 Using Enhanced Holtek C { _pd0 = 0 ; // stop the PWM function _pa = (_pa & 0x0f3) | 0x07 ; // PA2=1 : set PA2 to 1, enable a discharge operation } void a0_charging() // this function enables the battery charging function { _pa=(_pa&0x0f1)|0x01; // stop discharging, enable charging circuit _pd0=1; // enable the PWM output } //-------------------// stop_charging() //-------------------void stop_charging() // this function stops the charging { _pd0 = 0 ; // stop the PWM output _pa = (_pa & 0x0f3)|0x03; // stop charging } //----------------------------------------------// check_battery0_status() // check the status of battery 0, set/reset flags // I/P: bat0_voltage //-----------------------------------------------1 void check_battery0_status() 2{ 3 if( bat0_voltage < V01 ) // battery < 0.1V 4 bat0_on_flag = bat0_1c_charging_flag = bat0_voltage_over_flag = 0 ; 5 else if( bat0_voltage < V125 ) // 0.1V <= battery < 1.25V 6 { 7 bat0_on_flag = 1 ; 8 bat0_1c_charging_flag=0; 9 bat0_voltage_over_flag=0; 10} 11 else if( bat0_voltage < V155 ) // 1.25 <= battery <1.55 12{ 13 bat0_on_flag = 1 ; 14 bat0_1c_charging_flag=1; 15 bat0_voltage_over_flag=0; 16 } 17 else 18 { 19 bat0_on_flag=1; 20 bat0_1c_charging_flag=0; 21 bat0_voltage_over_flag=1; // battery >= 1.55 22 } 23 } ■ Program Description This function sets the related flags according to the acquired battery voltage values for later use. 3~4 5~10 Rev. 1.30 If the battery voltage is less than 0.1V, set the flag to 0 (battery not inserted) If the battery voltage is between 0.1V~1.25V, the battery is inserted but not yet 103 March 15, 2011 Using Enhanced Holtek C charged 11~16 If the battery voltage is between 1.25V~1.55V, the battery is inserted and is being charged 17 If the battery voltage is larger than 1.55V, the battery is fully charged //-----------------------------------// show_bat0_led() // set the battery 0 LED flag //-----------------------------------void show_bat0_led() // set the red LED according to the current battery voltage { led0_on_flag = 0 ; // yellow LED off led1_on_flag = bat0_on_flag ; // red LED, check if a battery is inserted into the charger } //-------------------------------------------------------// ad_4_data() : compute the average voltage of battery //-------------------------------------------------------void ad_4_data() { int i ; for( i = 0 ; i < 7 ; i++ ) // move battery value forward Vbattery[i] = Vbattery[i+1] ; Vbattery[i] = bat0_voltage ; // store current voltage Vbat0_old = Vbat0 ; // save old Vbat0 for( i = 0 , Vbat0 = 4 ; i < 8 ; i++ ) Vbat0 += Vbattery[i] ; Vbat0 >>= 3 ; // div 8 for average value save to Vbat0 } ■ Program Description This function is to calculate the battery voltage average value as read earlier. //-------------------------------------// get_Vbat0_inc80mv() //-------------------------------------void get_Vbat0_inc80mv() { if( Vbat0 >= (Vbat0_old + V80mv) ) // if the voltage exceeds the previous 80mV Vbat0 = Vbat0_old + V80mv ; // change the current voltage value } //----------------------------------// get_bat0_Vpeak() //-------------------------------void get_bat0_Vpeak() { if( Vbat0 < Vbat0_peak ) // if the voltage is less than the voltage peak value { if( Vpeak_cx >= Vpk_time ) // voltage peak value has persisted for 64 seconds { // voltage charged within 64 seconds is less than this peak value bat0_Vpeak_charging_ok = 1 ; // charging is ok when time>64 Vpeak_cx = 0 ; } } Rev. 1.30 104 March 15, 2011 Using Enhanced Holtek C else { Vbat0_peak = Vbat0 ; // update voltage peak value Vpeak_cx = 0 ; // peak value timer reset bat0_Vpeak_charging_ok = 0 ; // peak flag reset } } //--------------------------------------------// SetDischargingLEDflag -- bat0_discharging() //--------------------------------------------void SetDischargingLEDflag() // set the discharge LED flag { led1_on_flag = 1 ; // red LED on led0_on_flag ^= 1; // yellow LED flashing } //--------------------------------// bat0_500ma_charging() //--------------------------------1 void bat0_500ma_charging() 2{ 3 unsigned int temp; 4 5 charging_cx = 0; 6 a0_charging(); 7 _adcr = a2+ch3; // ADC channel 2 8 9 while(1) 10 { 11_emi=0; 12 _start=0; 13 _start=1; 14 _start=0; // start AD conversion 15 while(_eoc); 16 _emi=1; 17 18 temp = (unsigned int)_adrh ; 19 temp = (temp << 8)+(unsigned int)_adrl ; // read AD converted value 20 if((temp >= V260mv) && (_pwm != 0)) _pwm-- ; 21 if((temp < V250mv) && (_pwm != 0)) _pwm++ ; // if battery 22 // current<566ma,add pwm 23 24 if( charging_cx >= 240 ) return ; // if charging time >= 6 hours, return } 25 } ■ Program Description This function executes the quick charge operation during which the current will be adjusted according to the measured voltage. Rev. 1.30 5 Reset the charging time 6 Enable the charging mechanism 105 March 15, 2011 Using Enhanced Holtek C 7 Set channel2 of the AD converter to be the input channel for the battery voltage 9~20 Loop, read the voltage during the charging process and use it to adjust the current 11 Disable any interrupt generation 12~14 Enable the AD converter 15 Wait for completion of an AD conversion 16 Enable the interrupt 18~19 Read the voltage value 20 If the battery voltage exceeds 293mv and the PWM is not zero, then subtract 1 from the PWM value 21 If the battery voltage is less than 281mv and the PWM is not zero, then add 1 to the PWM value 23 If the charging time has reached 6 hours, stop and return 24 Keep charging //---------------------------------// bat0_50ma_charging() //---------------------------------1 void bat0_50ma_charging() 2{ 3 unsigned int temp; 4 5 charging_cx=0; 6 a0_charging(); //set led status's flag 7 _adcr = a2+ch3; //set AD channel 2 8 9 while(1) 10 { 11_emi=0; 12 _start=0; 13 _start=1; 14 _start=0; // start ADC conversion 15 while(_eoc); 16 17 _emi=1; 18 temp = (unsigned int)_adrh ; 19 temp = (temp << 8) + (unsigned int)_adrl ; // read AD conversion value 20 if( temp < V20mv ) 21 { 22 if(_pwm) _pwm++; // battery's current<58 mA,pwm add 23 else _pwm = 0x10; // minimum 24 } 25 else if( temp >= V30mv ) 26 { 27 28 if( _pwm ) _pwm--; // battery's current>=78 mA,pwm dec else _pwm=0x10; 29} 30 31 if( charging_cx >= 240 ) return ; // charging's time out 6 hours 32} 33 } Rev. 1.30 106 March 15, 2011 Using Enhanced Holtek C ■ Program Description Function to manage the slow charge operation. 5 Set the charging time to be 0 6 Enable PWM and the charging circuit 7 Set Channel 2 of the AD converter 9~32 Loop in battery charge process execution 11 Disable all interrupt generation 12~14 Enable the A/D converter 15 Wait for the completion of the A/D converter 16 Enable the interrupt 18~19 Read the data after A/D conversion 20~24 If the battery voltage is smaller than 29mv, add 1 to PWM 25~29 If the battery voltage is larger than or equal to 39mv, subtract 1 from PWM 31 If the charge time has reached 6 hours, stop and return 32 If there is still time remaining, keep charging //-------------------------------------// Ch0_Discharging() //-------------------------------------1 void Ch0_Discharging() 2{ 3 madc_on_sub(); // get battery's voltage and status 4 led0_on_flag = 0; // set LED flags 5 led1_on_flag = 1; // turn RED LED on, Yellow flash 6 bat0_charging_initial(); // initial 7 while(1) 8 { 9 a0_discharging(); // to discharge (PA2=1) 10 11 _delay(250000) ; // delay 0.25s 12 _delay(250000) ; 13 _delay(250000) ; 14 _delay(250000) ; // total delay 1s 15 16 madc_on_sub(); // get battery's voltage 17 SetDischargingLEDflag(); // set LED0 flag 18 19 ad_4_data(); // get battery's average voltage 20 if( Vbat0 >= (Vbat0_old + V80mv) ) 21 Vbat0 = Vbat0_old + V80mv ; 22 23 get_bat0_Vpeak(); // check peak value 24 bat0_discharging_ok_flag = (Vbat0 < V155) ? 1 : 0 ; // if battery < 25 // 1.15,discharging end 26 27 // if no battery in or battery charging is over or battery discharging is ok, then return if( !bat0_on_flag || bat0_voltage_over_flag || bat0_discharging_ok_flag ) 28 29 31 return ; // battery discharging ok,jump } 32 } Rev. 1.30 107 March 15, 2011 Using Enhanced Holtek C ■ Program Description Function to discharge the battery in the charger – the LED will be on and the yellow LED will flash. 3 Read the voltage of the battery in the charger and setup related flags 4~5 Set the red LED to be on and yellow LED off 6 Calculate the reference voltage value for the discharge process 7~31 Loop in the discharge process 9 Enable the discharge switch 11~14 Wait for 1 second 16 Enable the A/D converter, read the battery voltage and setup the related flags 17 Set the LED flags with the red LED on and the yellow LED off in sequence 19 Calculate the average voltage value after discharging 20~21 If this voltage is lower than the reference voltage by 80mv, update the voltage to be the reference voltage plus 80mv Rev. 1.30 23 Compare with the voltage peak value or update 24 Check, if the voltage is smaller than 1.55V, set the discharge completed flag 27 If the battery has been removed or discharging completed, then return 31 Continue with the discharging process 108 March 15, 2011 Using Enhanced Holtek C Chapter 9 Program Example – The HT46R74D-1 Tyre Pressure Gauge 9.1 Objective This device uses the HT46R74D-1 as the main MCU device used together with a sensor to implement a car tyre pressure gauge detector. The final high three bits of the effective value will be displayed on the LCD with four selectable display units (Psi, Bar, Kpa, Kgf/cm2.) To increase accuracy, it is recommended to construct a Sensor Output Voltage and Discharge Time (V-T) table with a higher number of measured values, for related calibration. 9.2 Functions ■ Switches One switch for unit select: SELECT One I/O control: controls an I/O pin used for operation indicator ■ Function Description 9.2.1 When the MCU is not powered on or in the halt mode, connect the power or press "SELECT" to wake up the MCU. The system will turn on the operating indicator as well as measuring the no-load air pressure value to save this as a calibration value (the calibration value is saved during the discharge time, Tc, under a fixed charge time of, Ti). 9.2.2 If there is no switch activation, use the average of ten charge time values minus the calibration value to be the measured Tc value (the discharge time Tc will be refreshed once per second). After a table read execution, proceed with the calculation according to the selected units and acquire the high three bits from the result as the effective value to be displayed on the LCD. If a switch activation is detected, enter the related switch handling routine. 9.2.3 If the displayed value is larger than 999, then a "---" will be shown to indicate that the measured value has exceeded the display range. 9.2.4 When the MCU is in the operating status, each time the "SELECT" switch is pushed, the display units will be changed. The display units will alternate in the following order: Psi=>Bar=>Kpa=>Kgf/cm2. At the same time the displayed values will show the corresponding new unit changes (for the units transform relationship refer to the appendix.) 9.2.5 When the MCU is in the operating status, if no switch is detected within 60 seconds, the LCD display will be switched off and the LCD driver I/O port cleared to zero and then the MCU will enter to enter the power-down mode. 9.2.6 Some parameter definition are modified according to the Sensor Output Voltage – Discharge Time (V-T) table as described below: #DEFINE PER 100 #DEFINE PSI_FULL 10000 #DEFINE BAR_FULL 689 #DEFINE KPA_FULL 0D54H #DEFINE KPA_FULL_H16 01H #DEFINE KGFCM2_FULL 703 #DEFINE TABLE_NUM 13 #DEFINE TABLE_PER 12 Note: Rev. 1.30 PER: Enlarge the sensor full scale value 100 times PSI_FULL: Sensor full scale value is 100(PSI) x100=10000 BAR_FULL: Sensor full scale value is 6.89(PSI) x100=689 KPA_FULL: Sensor full scale value is 689.48(PSI) x100=68948 low 16 bits 109 March 15, 2011 Using Enhanced Holtek C KAP_FULL_H16: Sensor full scale value is 689.48(PSI) x100=68948 high 16 bits KGFCM2_FULL: Sensor full scale value is 7.03(PSI) x100=703 TABLE_NUM: V-T table value number TABLE_PER: The V-T table in this device subdivides the sensor output into twelve sections, which is to build a set of values with 5mV intervals. 9.3 Circuit Diagram (Figure 1) Rev. 1.30 110 March 15, 2011 Using Enhanced Holtek C ■ Circuit Description The HT46R74D-1 is a dual slope A/D type micro controller which includes an internal amplifier, voltage follower, integrator and comparator. During the charge cycle, the internal MUX will be switched to the amplifier output and charge the charge/discharge capacitor. In the discharge cycle, the MUX will be switched to VDSO to discharge to discharge the capacitor. When the voltage drops to 1/6VDSO, the comparator will output a low level. This point is defined as the discharged state. The internal 3.3V reference voltage in the HT46R74D-1 can be used as a voltage source for the sensor while the VOBGP pin can provide the sensor with a 1.5V reference voltage. The output voltage of the sensor should be processed through an amplifier. The amplifier connection method is illustrated in figure 2: (Figure 2) Here, VDOPAO=VOBGP+(VA-VB)*(R2/R1)=1.5V+10*(VA-VB)(where R1=R3, R2=R4, R2=10R1). The sensor specification used by this application will output a voltage of 60mV for a full scale 100PSI value under an operating voltage of 3.3V. The V-T table uses (sensor output voltage/5mV+10) to be the offset address which includes 13 data sets. When measuring the tyre air pressure, the same charge time is listed in the table. Every measured discharge time can be looked up to confirm the approximate offset address and value. After the calculation, the sensor output voltage value should be taken to two decimals so as to improve the accuracy, and then the high three effective bits of the calculation result after the unit conversion will be displayed on the LCD. ■ Special Points to Note 9.3.1 LCD Display Interface (figure 3): Set the arrangement from left to right as: Digit1, Dot1, Digit2, Dot2, Digit3 and the four unit names. The corresponding layout is shown in the table: Rev. 1.30 111 March 15, 2011 Using Enhanced Holtek C (Figure 3) Pin Seg0 Seg1 Seg2 Seg3 Seg4 Seg5 Seg6 Seg7 Seg8 Com0 1f 1a Bar 2f 2a Kpa 3f 3a Kgf/cm² Com1 1e 1g 1b 2e 2g 2b 3e 3g 3b Com2 1d 1c Dot1 2d 2c Dot2 3d 3c Psi 9.3.2 Note that the amplifier must be setup according to the selected sensor specification. The sensor output signal after amplification by the amplifier should not exceed 4/6VDSO. 9.3.3 Select a proper charge/discharge resistor Vr, capacitor Vc and charge time Ti to make the Vc voltage remain between 1/6VDSO and 5/6VDSO. 9.3.4 To increase accuracy, it is recommended that a table is constructed with more measured data. 9.3.5 Factors such as charge time changes, different regular voltage output etc will affect the discharge time. The Sensor Output Voltage – Discharge Time (V-T) table should be noted for calibration. There are two interrupt control and status registers, INTC0, INTC1, provided to generate interrupt events after an A/D conversion. The other related registers and definitions are listed below, such as the CHPRC (Charge pump/Regulator Control) register which is used to control the on/off of the charge pump and voltage regulator. Bit No. Label Function 0 EMI Control the master (global) interrupt (1=enabled; 0=disabled) 1 EEI0 Control the external interrupt 0 (1=enabled; 0=disabled) 2 EEI1 Control the external interrupt 1 (1=enabled; 0=disabled) 3 ET0I Control the Timer/Event Counter 0 interrupt (1=enabled; 0=disabled) 4 EIF0 External interrupt 0 request flag (1=active; 0=inactive) 5 EIF1 External interrupt 1 request flag (1=active; 0=inactive) 6 T0F Internal Timer/Event Counter 0 request flag (1=active; 0=inactive) 7 — For test mode used only. Must be written as "0"; otherwise may result in unpredictable operation. INTC0 (0BH) Register Bit No. Label 0 ET1I Control the Timer/Event Counter 1 interrupt (1=enabled; 0=disabled) 1 EADI Control the ADC interrupt (1=enabled; 0:disabled) 2 ERTI Control the real time clock interrupt (1=enabled; 0:disabled) 3, 7 Function Unused bit, read as "0" 4 T1F Internal Timer/Event Counter 1 request flag (1=active; 0=inactive) 5 ADF ADC request flag (1=active; 0=inactive) 6 RTF Real time clock request flag (1=active; 0=inactive) INTC1 (1EH) Register Rev. 1.30 112 March 15, 2011 Using Enhanced Holtek C Bit No. Label 0 REGCEN 1 CHPEN 2 3~7 Function Enable/disable Regulator/Charge-Pump module. (1=enable; 0=disable) Charge Pump Enable/disable setting. (1=enable; 0=disable) Note: this bit will be ignored if the REGCEN bit is disabled BGPQST Bandgap quick start-up function 0: R short, quick start up 1: R connected, normal RC filter mode Each time REGCEN changes from 0 to 1, that is when the regulator turns on, this bit should be set to 0 and then set to 1 to ensure a quick start up. The minimum time to keep the bit low should be about 2ms. CHPCKD0~ Charge pump clock divider. These 5 bits form a clock divider with a division CHPCKD4 ratio range of 1 to 32. Charge Pump clock = (fSYS/16) / (CHPCKD+1) CHPRC (1FH) Register REGCEN CHPEN VOREG Pin OPA ADC Description Complete module is HiDisable disabled, OPA/ADC will have Impedance no Power 0 X OFF VDD OFF 1 0 OFF VDD ON 3.3V Active Used when V DD is greater than 3.6V 1 1 ON 2xVDD ON 3.3V Active Use whefor VDD is less than 3.6V (VDD=2.2V~3.6V) Bit No. 0 1~2 3 4~5 6 7 Rev. 1.30 Charge VOCHP Regulator Pump Pin Label Function Dual slope block (including input OP) power on/off switch. ADPWREN 0: disable Power 1: Power source comes from the regulator. Defines the ADC discharge/charge. 00: reserved ADDISCH0~ 01: charging - Integrator input connect to buffer output ADDISCH1 10: discharging - Integrator input connect to VDSO) 11: reserved ADCMPO Dual Slope ADC - last stage comparator output. Read only bit, write data instructions will be ignored. During the discharging state, when the integrator output is less than the reference voltage, the ADCMPO will change from high to low. ADC integrator interrupt mode definition. These two bits define the ADCMPO data interrupt trigger mode: ADINTM0~ 00: no interrupt ADINTM1 01: rising edge 10: falling edge 11: both edge ADC OP chopper clock source on/off switch. ADCCKEN 0: disable 1: enable (clock value is defined by ADCD register) ADRR0 ADC resistor selection 0: (VINT, VCMP)= (4/6 VOREG, 1/6 VOREG) 1: (VINT, VCMP)= (4.4/6 VOREG, 1/6 VOREG) 113 March 15, 2011 Using Enhanced Holtek C Bit No. Label 0 1 2 ADCD0 ADCD1 ADCD2 3~7 — Function Defines the chopper clock. ADCCKEN should be enabled. The suggested clock value should be around 10kHz.The chopper clock definitions are: 0: clock= (fSYS/32)/1 1: clock= (fSYS/32)/2 2: clock= (fSYS/32)/4 3: clock= (fSYS/32)/8 4: clock= (fSYS/32)/16 5: clock= (fSYS/32)/32 6: clock= (fSYS/32)/64 7: clock= (fSYS/32)/128 Reserved ADCD (1AH) Register The registers, ADCR and ADCD, are used to control the on/off function of the dual slope A/D converter, charge/discharge setting, ADC resistor selection and the chopper frequency setting. 9.4 MCU Configuration Options //-----------------------------------------------------------// Configuration Options: //----------------------------------------------------------//Osc: RC //fSYS: 4MHz //Wake_Up PA.0-7: PA.0 Wake_Up // PA.1-PA.7 Non Wake_Up //Pull-high PA: PULL-HIGH //Pull-high PB: PULL-HIGH //Pull-high PC: PULL-HIGH //Pull-high PD: PULL-HIGH //PA Buzzer Function: BZ/BZB Disable //Clock Source: T1 //Wdt: Disable //WDT Clock Source: T1 //CLR WDT: One clear instruction //WDT time-out period: 2^12/fs-2^13/fs //LVR: Disable //LVD: Disable //Int Function: Disable //LCD Driver Clock: IRCOSC/3 //LCD ON/OFF At HALT: LCD OFF At HALT //LCD Duty: 1/3 duty(3 com) //LCD Bias: 1/2 bias //----------------------------------------------------------- Rev. 1.30 114 March 15, 2011 Using Enhanced Holtek C 9.5 Program Flowchart START CALL INI_RAM CALL INI_IO SET F_ ON Setup related registers CALL SBR_ADC, test the discharge time under fixed charge time conditions Charge/discharge x 10 times, save the average value to [R_TMR1H, R_TIR1L] CALL SBR_TABLE, look up table for discharge time related offset address and table data CALL SBR_CALT, acquire the binary pressure value and save CALL SBR_DIS, LCD display Y N Y Rev. 1.30 F_HALT=0? N CALL SBR_KEY_SCAN CLR LED_IO, LED turned off CALL SBR_KEY_JUMP HALT Time up ? CLR F_HALT, set the status of each woken up register and I/O port 115 March 15, 2011 Using Enhanced Holtek C SBR_CALT Y F_ZERO_OUTPUT=1? Pressure value is 0.00 N RET Y N F_FULL_OUTPUT=1? R_ADDR1-R_ADJUST_H ->R_ADDR1 {[R_TABLE1_H,R_TABLE1_L] - [R_TMR1_H,R_TMR1_L}*100 CLR R_TEMP0 CLR R_TEMP1 CLR R_TEMP2 {[R_TABLE1_H,R_TABLE1_L] - [R_TMR1_H,R_TMR1_L}*100/ (R_TABLE1-R_TABLE2) N TO0R_ADJUST_L -> DATA0 N N R_ADDR1= R_ADJUST_H>1? Save the result as the calibration value Y Y CLR F_ON Y C=1? R_ADDR1=1 Y Pressure value is 0.00 RET F_ON=1? R_ADDR1= R_ADJUST_H>0? N Calculate the product of the decminal times 100 and the full scale value of each display unit Pressure value is 0.00 Above result/100, obtain the product of the decminal and the full scale value of each display unit RET Save the results from high to low into [R_TEMP2, R_TEMP1, R_TEMP0] Calculate the product of the integer and the full scale value of each display unit Save the result the decminal and integer to [R_TEMP2, R_TEMP1, R_TEMP0] B Rev. 1.30 116 March 15, 2011 Using Enhanced Holtek C B [R_TEMP2, R_TEMP1, R_TEMP0] /100/TABLE_PER Y Obtained value>999? N 999> Obtained value>100? Y Setup the decimal flag and registers N LCD display “---” 100> Obtained value>10? Y Set the decimal flag [R_TEMP2, R_TEMP1, R_TEMP0]/10-> [DATA2, DATA1, DATA0] N 10> Obtained value>1? Y Setup the decimal flag and registers N Obtained value<1? Y Set the decimal flag N [DATA2, DATA1, DATA0] /TABLE_PER DATA0<-TO0, DATA1<-TO1 RET CALL DA999, convert the hexadecimal value to a decimal number R_LCD3 <- TO0 R_LCD2 <- TO1 R_LCD1 <- TO2 RET Rev. 1.30 117 March 15, 2011 Using Enhanced Holtek C SBR_KEY_SCAN Initialise the debounce count units Initialise the timer0 register, enable the timer 4ms time up? N Y Disable timer0, clear the timer0 flag Switch pressed? F_KEY->F_KEY_TEM, Restore the debounce value N N Y Set flag F_KEY to 1 Clear flag F_KEY to 0 F_KEY=F_KEY_TEM? Y Debounce count units minus 1 Debounce count unit = 0? Y N F_KEY->F_KEY_PREV F_KEY=F_KEY_PREV? Y SET F_REPEAT CLR F_REPEAT RET Rev. 1.30 118 March 15, 2011 Using Enhanced Holtek C SBR_KEY_JUMP Y N F_KEY=0? 60S timing plus 1 CLR R_60S_L CLR R_60S_H 60 time up? N F_REPEAT=0? Y Set flag F_HALT to 1 Units change, set flag, but the other unit flags must not be changed RET RET Rev. 1.30 119 March 15, 2011 Using Enhanced Holtek C 9.6 Original Program and Program Description main.c 1 #include <ht46r74d-1.h> 2 #include “pubdef.h" 3 4 void InitialMCU(); // initial MCU 5 unsigned int SBR_ADC(); 6 void SBR_CALT_LOOP(); 7 void SBR_TABLE(); 8 unsigned long SBR_Calculate();//calculate the pressure value with different unit 9 void SBR_Display(unsigned int); //display 10 void SBR_KEY_SCAN(); //detect key 11 void SBR_KEY_JUMP(); //deal with key 12 13 unsigned char CurState ; // current state 14 unsigned int Tmr1Count ; // TMR1 count 15 unsigned int DischargeTime ; // discharge time 16 17 #pragma rambank0 18 bit bHalt ; // halt flag 19 extern bit bKeyPrev ; 20 #pragma norambank 21 22 void main(void) 23 { 24 unsigned char Seconds ; 25 unsigned int TotalTime, num ; 26 unsigned long pressure ; 27 char i ; 28 29 InitialMCU() ; 30 CurState |= POWER_ON ; //set power on or wake up state 31 32 while(1) 33 { 34 Seconds = 0 ; 35 TotalTime = 0L ; 36 DischargeTime = 0 ; 37 38 // calculate the average value of discharge time - average of 10 times 39 for( i = 0 ; i < 10 ; i++ ) 40 { 41 // get the discharge time Tc during the charge time Ti be fixed 42 TotalTime += SBR_ADC() ; 43 } 44 45 Rev. 1.30 DischargeTime = (TotalTime / 10) ; // get the average time 120 March 15, 2011 Using Enhanced Holtek C 46 while(1) 47 { 48 SBR_TABLE(); // to get the offset address of table with dichotomy 49 pressure = SBR_Calculate(); // calculate the pressure value with 50 // different unit 51 num = (unsigned int)pressure ; 52 SBR_Display(num); // display 53 54 if( bHalt ) //if at the mode of halt,jump to the halt mode 55 { 56 LED_IO = 0 ; // PA1 57 _halt(); //halt 58 bHalt=0; //wake up 59 bKeyPrev = 1; //set the previous key flag 60 _chprc = 0x63; //set 3.3V regular output 61 _adcr=0x41; //set the related registor 62 _adcd=0x07; 63 _tmr1c=0x88; 64 CurState = (PSI_STATE|POWER_ON) ; // set to 'psi' unit & power 65 // on/wake up 66 LED_IO=1; 67 break ; 68 } 69 70 SBR_KEY_SCAN(); //detect key 71 SBR_KEY_JUMP(); //deal with key 72 if(++Seconds == 50 ) //1S timing,jump to test the discharge time 73break ; 74 } 75} 76} ■ Program Description main.c 1 Insert the header file ht46r74d-1.h to define the microcontroller register addresses 2 Insert the header file public.h to define the constants and declare the global variables 4~11 Declare the data types of all functions and parameters 13~15 Define the global variables 17~20 The bit type variable must be defined in RAM bank0, so a preprocessor #pragma rambank0 and #pragma norambank must be added prior to/after the definition or declaration. 22 Main program starts 24~27 Declare local variables 29 Call InitialMCU function, clear the RAM space and setup I/O ports 30 Set the current status to be POWER ON 33~75 Main loop. The program will not jump out after entering the loop. 34~36 Set initial variable values, reset Seconds, TotalTime and DischargeTime 39~43 Loop x 10, acquire the total discharge time Tc under fixed charge time conditions and sum Rev. 1.30 121 March 15, 2011 Using Enhanced Holtek C 45 Divide the ten times total discharge time by 10 to acquire an average time 46~47 Internal loop 48 Find the associated time and voltage from the VT table according to the average discharge time as calculated above 49 Call function SBR_Calculate() to calculate the tyre pressure value corresponding to the discharge time and subdivide the tyre pressure value into three decimal digits according to the selected units. 52 Call function SBR_Display(num) and display the number on the LCD 54 If no switch is detected within 60 seconds, the system will enter the power-down mode with the bHalt flag set to 1 55 System enters the power-down mode 56 Turn off the operating indicator (LED) 57 System enters the power-down mode until a switch is pressed to be wake-up and return to the step following this location . 58~59 System returns here after being woken up and clears the pause flag to 0 and set the switch flag to 1 60~63 Reset the system structure to a 3.3V voltage output, setup dual slope A/D converter and clock initial values 64 Set the current status to have tyre pressure unit: PSI, system: POWER ON 66 Turn on the operating indicator LED 67 Jump back to the main loop, re-read the switch or discharge time 70 If the system is not in the power-down mode, continue to check if there are any switch inputs, call function SBR_KEY_SCAN 71 Call function SBR_KEY_JUMP and check and process the switch input and setup related flags 72 If one second has elapsed, return to the main loop to re-check the charge time and status, or stay in the internal loop displaying the previous pressure value and detecting the switch input. subroutine.c original program 1 #include <ht46r74d-1.h> 2 #include “pubdef.h” 3 unsigned long PresUnit ; 4 unsigned char DischargeIdx ; 5 unsigned char AdjustValue ; 6 unsigned char AdjustIdx ; 7 unsigned char LcdBuf[0x10] @ 0x140 ; 8 9 #pragma rambank0 10 bit bLED_Dot1 ; 11 bit bLED_Dot2 ; 12 bit bKeyPrev ; 13 bit bKeyFlag ; 14 bit bRepeat ; 15 #pragma norambank 16 17 extern unsigned char CurState ; // current state 18 extern unsigned int Tmr1Count ; // TMR1 count 19 extern unsigned int DischargeTime ; // discharge time 20 Rev. 1.30 122 March 15, 2011 Using Enhanced Holtek C 21 const unsigned char LEDDigitalPattern[11] = 22 { 23 0x5f,0x48,0x3e,0x7c,0x69, /*0~4*/ 24 0x75,0x77,0x58,0x7f,0x7d, /*5~9*/ 25 0x20 /*’-’*/ 26 } ; 27 28 const unsigned int VT_Table[TABLE_NUM] = { 29 /* ************* V-T table *************** */ 30 31 32 0x197E, 0x180b, 0x16ce, 0x1590, 0x1453, /*0,5,10,15,20;;0~4mv*/ 0x1315, 0x11d8, 0x109a, 0x0f5d, 0x0E1f, /*25,30,35,40,45;;5~9mv*/ 0x0ce2, 0x0ba4, 0x0a66 /*50,55,60;;10~14mv*/ 33 }; 34 void InitialMCU() 35 { 36 char i ; 37 char *mptr ; 38 39 ClearMemory() ; // Clear RAM bank 40 41 42 _intc0 = 0; 43 _intc1 = 0 ; // interrupt disable 44 _chprc = 0x63; // set 3.3V regular output, clock divider=12 45 _adcr = 0x41; // set the related registor 46 _adcd = 0x07; 47 _tmr1c = 0x88; // set TMR1 : Timer mode, prescale stages: f(int1)=f(t1) 48 49 _pac=0xff; // set PA to input mode 50 _pbc=0xff; // set PB to input mode 51 LED_IO_C=0; // set PA.1 to output mode 52 LED_IO=1; // turn LED off 53 54 CurState = PSI_STATE ; // initial state : PSI unit 55} Rev. 1.30 123 March 15, 2011 Using Enhanced Holtek C ■ Program Description InitialMCU This function is to initialise the system. 3~6 Define the global variable PresUnit (pressure unit), Dischargeldx (the index number where the discharge time is located), AdjustValue (adjustment value), Adjustldx (index number of the adjustment value) 7 Define the LCD memory buffer from address 0x140 to 0x14F 9~15 Use the preprocessor to define the global variables bit type, which should be defined in bank0 of the RAM. Lines 9 and 15 should be deleted if using the HI-TECH C compiler. 17~19 Declare the used external variables: CurState, Tmr1Count, DischargeTime 21~26 Define the LED character constant table LEDDigitalPattern, where the representative figures are ‘0’ ~ ‘9’ and ‘-‘ 28~33 Define the VT table constant, Tc, this time is proportional to the air pressure 34~44 Function InitialMCU is to clear bank0 and bank1 of RAM to 0 Setup the initial values of the other registers; disable the interrupts, setup the 3.3V regular output and set the clock divider to 12. 45 Set the ADCR register (0x18) to the discharge status with the power source provided by the regulator 46 Set the ADCD register (0x1A), set the chopper frequency to ((fSYS/32)/128) 47 Set Timer 1 to the timer mode and the prescale stage as f(int1)=f(t1) 49~50 Set both the I/O port A and B to inputs 51 Set the operating indicator (controlled by the 1st bit of port A) to an output 52 Turn off the operating indicator 54 Set the current air pressure units to be PSI Original Program SBR_ADC() //----------------------------------------------//--- SBR_ADC --// get the discharge time Tc during the charge time Ti be fixed // O/P: time //----------------------------------------------1 unsigned int SBR_ADC() 2{ 3 unsigned int TcTime ; 4 5 _t1on=0; // turn TMR1 off 6 _tmr0c=0x8f; // 32us(fSYS=4MHz), TMR0 -> timer mode, fSYS 7 _tmr0=0; 8 _adcr=0x4b; // at charge mode, ADC OP chopper clock source 9 _t0on=1; // TMR0 on 10 11 12 while(!_t0f) ; //charge and let Vc work over 4/6 VDSO, TMR0 //interrupt (delay) 13 _t0f=0; 14 _t0on=0; 15 _adcr=0x4d; // set : discharge mode 16 while(_adcmpo) ; // ADCMPO high->low if integrator output is less 17 // than the reference voltage 18 Rev. 1.30 _tmr0=0x83; 124 March 15, 2011 Using Enhanced Holtek C 19 _adcr=_adcr^0x06; // set : charge mode 20 _t0on=1; // trun TMR0 on 21 _t1on=0; 22 _tmr1l=0; // clear TMR1 23 _tmr1h=0; 24 while(!_t0f) ; // delay for charge 25 26 _adcr=_adcr^0x06; // set: discharge mode 27 _t1on=1; // TMR1 on, TMR0 off 28_t0on=0; 29_t0f=0; 30 while(_adcmpo) ; //discharge 31 _t1on=0; // TMR1 off 32 33 // return discharge time 34 TcTime = (unsigned int)(_tmr1h) ; // read TMR1 high byte 35 TcTime = (TcTime<<8) | _tmr1l ; // read TMR1 low byte and append to int 36 // variable 37 return( TcTime ) ; // get discharge time 38 } ■ Program Description This function is to obtain the discharge time under a fixed charge time from the dual slope A/D converter so as to obtain the air pressure value. 5 Disable timer1 6 Set Timer 0 to the timer mode, fSYS as the clock oscillator source with a division rate of 128 (when the system frequency is 4MHz, the clock has a period of 32us) 7 Clear the Timer 0 count register to 0 8 Enable the A/D, the A/D OP chopper and initiate the charging cycle 9 Enable Timer 0, time is 256x32us=8ms 11 Wait for Timer 0 to generate an overflow interrupt and detect using a polling method (wait for fully charged condition) 13 Clear the interrupt flag 14 Disable Timer 0 15 Set the A/D OP chopper to the discharge cycle 16 Check if the comparator output has changed, which is the point where the value is lower than the reference voltage. Stay in the loop until the comparator output switches. At this point jump out of the loop. . 18 Set the counter register of Timer 0 (0x83) 19 Enable the A/D OP chopper and enter the charging cycle 20 Enable the Timer 0 timer function 21~23 Disable Timer 1, clear Timer 1 counter register to 0 Rev. 1.30 24 Wait for Timer 0 to generate an overflow interrupt (0xff-0x83+1) x32=4ms) 26 Enable the A/D OP chopper and enter the discharge cycle 27 Enable Timer 1 28 Disable Timer 0 29 Clear the Timer 0 interrupt flag 30 Check if the comparator output has switched which is the point where the voltage is lower than the reference voltage. Stay in the loop until the comparator output 125 March 15, 2011 Using Enhanced Holtek C switches and then jump out. 31 Disable Timer 1 34~35 Read the Timer 1 counter register contents (=discharge time) 37 Save the discharge time Original Program SBR_TABLE //---------------------------------------------------------//--- SBR_TABLE --// get the opposite offset address of table with dichotomy // if DischargeTime >= VT_TABLE[i], then i-1 is the canditate // the order in VT_TABLE[] is decreasing order //----------------------------------------------------------1 void SBR_TABLE() 2{ 3 char i ; 4 5 AdjustValue = 0 ; // adjust value 6 AdjustIdx = 0 ; // adjust index 7 8 for( i = 0 ; i < TABLE_NUM ; i++ ) 9 { 10 if( DischargeTime >= VT_Table[i] ) // find the proper discharge time 11 { 12 DischargeIdx = !i ? i : i-1 ; 13 CurState &= ~KEY_TEMP ; // reset key temp flag 14 CurState &= ~FULL_OUTPUT ; // clear FULL output state 15 if( !i ) 16 CurState |= ZERO_OUTPUT ; // set zero output 17 18 if( (CurState & POWER_ON) != 0 ) // power on state 19 CurState &= ~POWER_ON ; // clear bit 20 21 22 return; } 23 } 24 25 // i = TABLE_NUM 26 DischargeIdx = TABLE_NUM -1 ; 27 CurState &= ~( KEY_TEMP | ZERO_OUTPUT) ; // reset key temp flag & zero output 28 CurState |= FULL_OUTPUT ; // set FULL output 29 if( (CurState & POWER_ON) != 0 ) 30 { 31 CurState &= ~POWER_ON ; 32 AdjustIdx = DischargeIdx ; 33 } 34 } Rev. 1.30 126 March 15, 2011 Using Enhanced Holtek C ■ Program Description This function is used to find the discharge time using the VT table to find a value equal to or close to the actual discharge time. There are 13 reference time values in the VT table, each one corresponding to a different air pressure value. Save the time found to the reference time variable and use this time as the table index reference pointer. 5~6 Clear the calibration value and pointer to 0 8~23 Loop, search for a value equal to or close to the actual practical discharge time in the VT table. The table contains 13 premeasured pressure values corresponding to the discharge time value, for which they can be checked in order from high to low is to compare the first value of the table to the last one. If any eligible value is found, then stop the comparison and return to the original calling location. 10~22 If the discharge time is larger than or equal to the table value, then it means the stored reference pointer has been found. The record reference pointer (Dischargeldx) is the index of the value location. Delete the switch flag. The discharge time will not be smaller than the last value, so the air pressure will not exceed the highest table value. Clear the flag FULL_OUTPUT(exceeds the highest air pressure). If the discharge time is larger than or equal to the first table value, it means the air pressure is lower than the 13 effective values in the table, so setting the flag ZERO_OUTPUT means the air pressure is smaller than the lowest reference value. 18~19 If the current operating status is POWER ON, clear the POWER ON flag 21 Return to the original calling location 25 If progam execution arrives at this point, it means the discharge time is lower than the table value (the air pressure is too high) 26 Setup the reference pointer (DischargeIdx) to be at the last table index location 27 Clear the KEY_TEMP and ZERO_OUTPUT flags 28 Setup the FULL_OUTPUT flag 29~33 If the current operating status is POWER ON, clear the POWER ON flag, setup the calibration pointer 34 Return to the original calling location ■ Original Program SBR_CALCULATE //----------------------------------------------//--- SBR_Calculate --// calculate pressure with different unit and pressure number // O/P: pressure number //----------------------------------------------1 unsigned long SBR_Calculate() 2{ 3 unsigned long rtmp, rtmp1, Tslice ; 4 unsigned char rate ; 5 6 if( (CurState & ZERO_OUTPUT) == ZERO_OUTPUT ) // it's Zero output 7 { //for zero output of sensor 8 bLED_Dot1 = 1 ; //to display 0.00 9 bLED_Dot2 = 0 ; 10 return(0L) ; 11 } 12 Rev. 1.30 13 if( (CurState & FULL_OUTPUT) == FULL_OUTPUT ) 14 { 127 March 15, 2011 Using Enhanced Holtek C 15 DischargeIdx -= AdjustIdx ; //for full output of sensor 16 rtmp = 0 ; 17} 18 else 19 { 20 Tslice = (unsigned long)((VT_Table[DischargeIdx] – DischargeTime) * 21 100) ; 22 rate = (unsigned char)(Tslice / (VT_Table[DischargeIdx] - 23 VT_Table[DischargeIdx+1])) ; 24 25 if( CurState & POWER_ON ) 26 { 27 CurState &= ~POWER_ON ; // clear flag 28 AdjustValue = rate ; 29 AdjustIdx = DischargeIdx ; 30 } 31 32 if( rate < AdjustValue ) 33 { 34 if( DischargeIdx >= AdjustIdx ) 35 { 36 bLED_Dot1 = 1 ; //to display 0.00 37 bLED_Dot2 = 0 ; 38 return(0L) ; 39 } 40 DischargeIdx--; 41 } 42 else rate -= AdjustValue ; 43 44 if( DischargeIdx < AdjustIdx ) // DischargeIdx<R_ADJUST_H 45 { 46 bLED_Dot1 = 1 ; //to display 0.00 47 bLED_Dot2 = 0 ; 48 return(0L) ; 49 } 50 51 DischargeIdx -= AdjustIdx ; // DischargeIdx > R_ADJUST_H 52 SBR_CALT_LOOP(); // O/P: PresUnit 53 rtmp = (PresUnit * rate) / 100 ; 54 } 55 56 SBR_CALT_LOOP(); 57 rtmp += PresUnit * DischargeIdx ; 58 rtmp1 = rtmp / (PER * TABLE_PER) ; // 12 * 100 R_TO0, R_TO1 = rtmp1 59 Rev. 1.30 60 if( rtmp1 > 100 ) // rtmp1 > 100 61 { 128 March 15, 2011 Using Enhanced Holtek C 62 bLED_Dot1 = bLED_Dot2 = 0 ; 63 return(rtmp1) ; 64 } 65 if( rtmp1 > 10 ) // 100>= rtmp > 10 66 { 67 bLED_Dot1 = 0 ; //>10 & <100 68 bLED_Dot2 = 1 ; 69 70 return( rtmp / (10 * TABLE_PER) ) ; } 71 72 bLED_Dot1=1; 73 bLED_Dot2=0; 74 return( rtmp / TABLE_PER ) ; 75 } ■ Program Description SBR_CALCULATE This function is to calculate the air pressure value based on the above reference time, pointers and actual discharge time along with the selected pressure unit. The calculation method uses the thirteen time values in the VT table, which means that there are twelve equivalent intervals from 0.00 pressure to the highest pressure value. Each pressure unit has its own full scale value, so the nth interval pressure value is (n=0, 1,..12) n*(full scale value/12) while the actual discharge time is not necessarily equal a reference time (and will be larger than a reference time). Therefore this small part must be added. First calculate the percentage that it occupies in the interval and then multiply it by the average pressure value of the interval. As the calculated time uses char, long etc integer types variables (floating types could also be considered) multiplication should be carried out first to amplify the value by 100 times. The following formula is used: rate = [(reference time – actual discharge time) *100] / [reference time – next reference time] pressure1 = [rate * (full scale value/12)] /100 (rate and full scale values both multiplied by 100) [reference time – next reference time] indicates the range of the actual discharge time, namely reference time > actual discharge time >= next reference time The full scale value in the program has been multiplied by 100 so as to add with pressure1, therefore the final total value should be divided by 100. The final total air pressure value = (n * (full scale value/12) + pressure1) / 100 The order of calculation in the program can be different however the principles are the same. 7~11 If the flag ZERO-OUTPUT is set, it means the discharge time exceeds the highest value in the VT table (the air pressure is lower than the effective value.) In this case the pressure value cannot be displayed. Set the display to be 0.00. Return a value of 0. 13~17 If the flag FULL_OUTPUT is set, it means the discharge time is lower than the smallest value in the VT table (the air pressure is higher than the effective value in the table). With reference pointer minus calibration value pointer, set the air pressure offset value to 0, and jump to line 57 to calculate the total air pressure value. 18~54 Neither ZERO nor FULL, which means the discharge time is located within the table values. Execute the following line 20~23 Calculate the air pressure offset value rate. rate = ((reference time – actual discharge time) * 100) / reference time difference = VT_Table [reference pointer] – VT_Table [reference pointer +1] Rev. 1.30 129 March 15, 2011 Using Enhanced Holtek C 25~30 If the current operating status is POWER_ON, clear the POWER_ON flag, set the calibration value = air pressure offset rate. Set the calibration pointer. 32~41 If the air pressure offset rate is smaller than the calibration value, check the reference pointer. If the reference pointer is smaller or equal to the calibration pointer, set the air pressure display value to 0.00 with the first decimal point displayed. Return to the calling place and decrease the reference pointer by one. 42 If the air pressure offset rate is larger than the calibration value, subtract the calibration value from the air pressure offset value. 44~49 Check the reference pointer. If smaller than the calibration pointer, the display value will be 0.00. Set to display the decimal point, return to the calling location. 51 Subtract the calibration pointer from the reference pointer 52 Obtain the full scale value of the current air pressure unit 53 Calculate air pressure offset value = (air pressure offset value * full scale value) /100 (not yet divided by 12) 56 SBR_CALT_LOOP() obtains the full scale value of the air pressure unit 57~58 Air pressure total value = [air pressure offset value + (full scale value * reference pointer)] / (12*100) Dividing by 12 is to equal the full scale value divided into twelve average value intervals. The 100 is to amplify during the calculation. 60 Determine whether to display the decimal point or not based on the air pressure total value 60~64 If the air pressure value is larger or equal to 100 (three digits), then no need to display any decimal points. Return this air pressure value. 65~70 If the air pressure value has a value between 99~10 (two digits), it is necessary to display the second decimal point. Re-calculate the air pressure total value by dividing by 10 (take three digits) only, and return to the original calling location Air pressure total value = [air pressure offset value + (full scale * reference pointer)] / (12*10) 72~74 If the air pressure total value is between 9 and 1, display the first decimal point Dot1=1 The air pressure total value does not need to be divided by 100 and now air pressure total value = (air pressure offset value + (full scale value * reference pointer)) / 12 Return to the original calling location ■ Original Program SBR_CALT_LOOP //----------------------------------------------//--- SBR_CALT_LOOP --//SBR for SBR_CALT get the pressure of current unit //----------------------------------------------1 void SBR_CALT_LOOP(void) 2{ 3 switch( CurState & 0x0f ) // check bit0~4 4 { 5 6 7 8 Rev. 1.30 case PSI_STATE : // if unit is Psi PresUnit = PSI_FULL ; // unsigned long break ; case BAR_STATE : //if Bar 130 March 15, 2011 Using Enhanced Holtek C 9 PresUnit = BAR_FULL ; 10 break ; 11 case KPA_STATE : //if Kpa 12 PresUnit = KPA_FULL_H16 ; 13 PresUnit = KPA_FULL | (PresUnit << 16) ; 14 break ; case KGFCM2_STATE : //if Kgf/cm2 15 16 PresUnit = KGFCM2_FULL ; 17 break ; 18 } 19 } ■ Program Description SBR_CALT_LOOP This function provides the full scale value in the current air pressure units 3 Analyse the current air pressure unit 5~7 Unit = PSI, give the variable PresUnit to the PSI full scale value 8~10 Unit = Bar, give the variable PresUnit to the Bar full scale value 11~14 Unit = Kpa, give the variable PresUnit to the Kpa full scale value 15~17 Unit = Kgf/cm2, give the variable PresUnit to the Kgf/cm2 full scale value Original Program SBR_KEY_SCAN //----------------------------------------------//--- SBR_KEY_SCAN --//--- detect key --//----------------------------------------------1 void SBR_KEY_SCAN() 2{ 3 unsigned char Debounce_count ; 4 5 Debounce_count = R_20MS ; // detect key for 20mS 6 CurState &= ~KEY_TEMP ; // clear KEY_TEMP flag 7 _tmr0c = 0x0A0; // set TMR0 to Timer mode, internal clock (Intl. 8 Rev. 1.30 // RC), disable 9 while(1) 10 { // wait TMR0 time out 11 _tmr0 = 208 ; // 4mS 12 _t0on = 1 ; // turn TMR0 on 13 while( !_t0f ) ; // wait internal timer/event counter 0 14 // interrupt flag 15 _t0f = 0 ; // clear internal timer/event counter 0 request flag (0b.5) 16 _t0on = 0 ; // turn TMR0 off 17 // check input key 18 bKeyFlag = KEY_IO ? 0 : 1 ; // check if key press or not(key no), PA.0 19 // set flag 20 if( (bKeyFlag && (CurState & KEY_TEMP)==0) || 21 (!bKeyFlag && (CurState & KEY_TEMP)!=0) ) 22 { 23 CurState ^= KEY_TEMP ; // toggle flag KEY_TEMP 24 Debounce_count = R_20MS; // renew 131 March 15, 2011 Using Enhanced Holtek C 25 continue ; 26 } 27 28 if( --Debounce_count == 0 ) 29 break ; 30 } 31 32 Debounce_count = R_20MS; 33 if( bKeyFlag ) // input key 34 { 35 if( bKeyPrev ) // has pressed previously 36 bRepeat = 1; // set repeat flag 37 else 38 { 39 bRepeat =0; 40 bKeyPrev = 1; 41 } 42 } 43 else // no input key 44 { 45 if( bKeyPrev ) 46 bRepeat = bKeyPrev = 0 ; 47 else bRepeat = 1; 48 49 } 50 51 52 CurState &= ~(ZERO_OUTPUT | FULL_OUTPUT | KEY_TEMP) ; // clear flags } ■ Program Description SBR_KEY_SCAN Check if there is a switch input. 5 Set the detection time to be 20ms (Timer 0 generates an interrupt at every 4 ms, Debounce_count=5, total of 5 times, 20ms). Detect a certain switch input within the time (eliminate the key bounce mistake) 6 Clear the KEY_TEMP flag in the current operating status 7 Set the Timer 0 as timer mode; use the internal clock oscillator (12 KHz), prescaler 1:1 to pause timing 9~30 The loop, wait for a switch input 11 Set the interrupt interval of the Timer 0 to be 4ms (TMR0 = 208)(256-208)*(1/12 KHz) =4ms 12 Enable Timer 0 13 The loop, wait for Timer 0 to generate an overflow interrupt 15 Clear the interrupt request flag of Timer 0 16 Disable Timer 0 18 Check if there is any switch input (port A bit 0) and setup flag bKeyFlag 20~26 If a switch input is detected this time but none is detected before this, or a situation countering to this occurs, it means there is no effective switch input, change the current operating status to be the switch status, reset the detection time back to the start of the loop, continue to check the switch status Rev. 1.30 132 March 15, 2011 Using Enhanced Holtek C 28~29 If the detection time has been down to zero, jump out of the loop 30 Or continue to detect the switch input 32 Set the detection to be 5 times 33~34 If the switch input is considered effective, analyze the switch status 35~36 If the switch has been input before, set the bRepeat flag (repeat) 37~41 Or clear the bRepeat flag, set the bKeyPrev flag (switch input occurred) 43~49 If there is no switch input, analyze the status as below 45~46 Or set the flag bRepeat 51 Clear the flags in the current operating status, ZERO_OUTPUT, FULL_OUTPUT and KEY_TMEP 52 Return to the original calling location ■ Original Program SBR_KEY_JUMP //----------------------------------------------//--- SBR_KEY_JUMP --//--- deal with key --//----------------------------------------------1 unsigned int ElapseTime ; 2 3 void SBR_KEY_JUMP() 4{ 5 unsigned char tmp ; 6 7 if( !bKeyFlag ) // no input key 8 { 9 ElapseTime++ ; 10 if( ElapseTime >= 0x0bb8 ) // 60 seconds 11 bHalt = 1 ; // set halt flag 12return ; 13} 14 // has input key 15 ElapseTime = 0 ; 16 if( !bRepeat ) // if no repeat key, advance to next unit 17{ 18 tmp = CurState & 0x0f; // current unit 19 tmp = (tmp == KGFCM2_STATE) ? PSI_STATE : (tmp << 1) ; 20 // change to next unit state 21 CurState &= 0xf0 ; // clear bit0 ~ 3 22 CurState |= tmp ; // set to next unit 23} 24 } ■ Program Description 1 Define ElapseTime as the time elapsed 7~13 When there is no switch input, accumulate the time and check if it has elapsed for 60 seconds 10~11 If it has elapsed for 60 seconds (0x0bb8=3000, 3000x20ms=60s) without any switch input, set the flag bHalt = 1 Rev. 1.30 13 Return to the original calling place 14 If there is a switch input, clear the variable ElapseTime, analyze the switch 133 March 15, 2011 Using Enhanced Holtek C 16~23 If the switch input is not repeated, execute the following execution Change the air pressure unit to be the next counting unit with Psi => Bar => Kpa => Kgf/cm2 by turns. Set the new unit to be in the variable of the current operating status. 24 If the switch input is repeated, no further action ■ Original Program SBR_DISPLAY //----------------------------------------------------------------//--- SBR_Display --//--- display the presure value( 3 decimal digit and unit scaler) --// I/P: value = number to be displayed (decimal) // write digit pattaern to LCD RAM buffer, 140H ~ 148H //----------------------------------------------------------------1 const char divi[3] = { 100, 10, 1 } ; 2 3 void SBR_Display(unsigned int value) 4{ 5 char i, num, pattern, flag ; 6 // display digit 7 for( i = flag = 0 ; i < 3 ; i++ ) // one digit uses 3 LCD RAM bytes 8 { 9 num = value / divi[i] ; // separate the input value 10 value = value % divi[i] ; 11 if( (num > 9) || (flag==1) ) 12 { 13 flag = 1 ; 14 num = 10 ; 15 } 16 pattern = LEDDigitalPattern[num] ; // map the pattern of digit number 17 LcdBuf[3*i] = pattern & 0x7 ; // low nibble for d/e/f segment 18 LcdBuf[3*i+1] = (pattern >> 4) & 0x07 ; // high nibble for c/g/a segment 19 20 num = ((pattern & 0x08)!=0) ? 0x02 : 0 ; // b segment 21 switch( i ) 22 { 23 case 0 : // first digit (left) 24 if( (CurState & BAR_STATE) != 0 ) num |= 0x01 ; 25 if( bLED_Dot1 ) num |= 0x04 ; 26 break ; 27 Rev. 1.30 case 1 : // second digit 28 if( (CurState & KPA_STATE) != 0 ) num |= 0x01 ; 29 if( bLED_Dot2 ) num |= 0x04 ; 30 break ; 31 case 2 : // third digit (right) 32 if( (CurState & KGFCM2_STATE) != 0 ) num |= 0x01; 33 if( (CurState & PSI_STATE) != 0 ) num |= 0x04 ; 34 break ; 35 } 36 LcdBuf[3*i+2] = num ; // write (display) the 3rd byte 134 March 15, 2011 Using Enhanced Holtek C 37 } 38 } ■ Program Description 1 Define the division which is 100, 10 and 1 3 This function will divide the input value into a decimal digit to be displayed on the LCD 7~37 Loop, display three digits and the decimal point 9 Calculate in turn the digits in hundreds, tens, and ones (starting from the hundreds) 10 Keep the remaining value 11~15 If the digits exceeds 9 or the input value is larger than 999, the display will show ‘---’ flag = 1, record the value over 999; num = displayed value 16 Obtain the corresponding digit pattern from the table LEDDigitalPattern 17~18 Write the pattern to the LCD memory address, display the digit, write the three bytes of the LCD memory to each of the digit pattern which refers to the LCD front pattern. Bits 0, 1 and 2 of the first byte stored segment f, e, d; Bits 0, 1 and 2 of the second byte store segment a, g, c; Bit 1 of the third byte stores segment b and the other bytes store the first and second decimal points and the air pressure unit name . 20 Obtain the segment b value from the pattern 21~35 According to their values determine if dot1, dot2 and the units are to be displayed . If it is in hundreds (i=0), analyse whether to display the first decimal point and the BAR unit. If it is in tens (i=1), analyse whether to display the second decimal point and the KPA unit. If it is in ones (i=2), analyse whether to display the KGF/CM or PSI unit. 36 Write to the third data byte of the LCD memory ■ Appendix N/m2 (Pa) kgf/m2 N/m2 (Pa) 1 2 kgf/m kgf/cm 98.0665*10 bar 1*105 bar atm 0.101972 10.1972*10-6 1*10-5 9.80665 2 kgf/cm2 1 3 1*10 1*10 4 10197.2 5 -4 mmH2O 0.986923*10-5 0.101972 9.80665*10 -5 9.67841*10 -5 1*10 lb/in2,psi mmHg 7.50062*10-3 145.038*10-6 -8 3 0.0735559 0.00142233 1 0.980665 0.967841 10*10 735.559 14.2233 1.01972 1 0.986923 10.1972*103 750.061 14.5038 atm 1.01325*10 10332.3 1.03323 1.01325 mmH2O 0.101972 1*10-8 1*10-4 9.80665*10-5 9.67841*10-5 1 mmHg 133.322 13.5951 0.00135951 0.00133322 0.00131579 13.5951 1 0.0193368 0.0703072 0.0680462 703.072 51.7151 1 lb/in2,psi 6.89476*103 703.072 0.0689476 1 3 10.3323*10 760 14.6959 73.5559*10-3 1.42233*10-3 Note: Atmospheric pressure (at) = 1kgf/cm2 The pressure indicated by H2O uses the density value of pure water at 4° C as the standard. Rev. 1.30 135 March 15, 2011 Using Enhanced Holtek C Chapter 10 Mixed Language –Assembly and C When implementing control of external peripherals, using assembly rather than C may result in higher execution efficiency. In such cases it may be beneficial to take a mixed language approach to the application program. One way to do this is to use inline assembly in C, a method which has been described in Chapter 4.9.3. The other method is to use a function call method. This chapter will introduce how to implement these methods by calling assembly language from within C and also calling C from within assembly language. The following items and rules should be followed otherwise some functions may not execute properly. → Naming of variables, functions and parameters → Parameter passing → Return value setup 10.1 Naming of Variables, Functions and Parameters ■ Difference in Names → Words in C are Case-sensitive. For example, count and Count are considered as two different words. → Words in assembly language are Case-insensitive. For example, Length and length have the same meaning. ■ Global variables and function names after compilation →→ When compiling global variables and functions with C, an underscore character will be added in front of the original name. The global variable count will become _count after compilation. The global variable Length will become _Length after compilation. The function GetTotalSize() will become _GetTotalSize() after compilation. →→ After the assembly program has been compiled, the name will be changed to upper case, such as Variable count will be changed to COUNT Variable Length will be changed to LENTGH Function GetTotalSize() will be changed to GETTOTALSIZE() ■ Function Parameter Names after compilation The function parameter name will be changed to the same name as the function itself after compilation but will be attached with an index number. For example, the parameter x in the function char GetSum(char x, char y) will be changed to GetSum0 while the parameter y will become GetSum1. When C calls functions, ex. z = GetSum(1,4), the C compiler will save the first parameter value 1 to the variable GetSum0, and the second parameter value 4 to GetSum1. 10.2 Parameter Passing As the MCU stack cannot be used by the application program, the C compiler will save the function parameters to the RAM Data Memory during parameter passing. No matter what type the data is, they will all be defined in the byte format. For example, for void SetPos(int xpos, in typos), the data parameter data type xpos and ypos is int (2 bytes) and will be changed to SetPos0 and SetPos1 for which the format defined in the RAM Data Memory is: SetPos0 DB 2 dup(?) ; xpos SetPos1 DB 2 sup(?) ; ypos The function SetPos will read the parameter value from the variable SetPos0 and SetPos1. Rev. 1.30 136 March 15, 2011 Using Enhanced Holtek C 10.3 Return Value Setting The C return value should, according to the return data type, decide which resources to use for transmission media. The table below lists the registers or variables where the returns values are saved. Return Type Return Value LBLW HBLW LBHW HBHW void 0 — — — — char 1 (byte) ACC — — — int/short 2 (byte) ACC RH — — long/float 4 (byte) ACC RH RM RU ACC A register RH, RM, RU variable Low bit character and low byte Return value Low Byte of the Low Word (LBLW) Low bit character and high bye Return value High Byte of the Low Word (HBLW) High bit character and low byte Return value Low Byte of the High Word (LBHW) High bit character and high byte Return value High Byte of the High Word (HBHW) 10.4 Calling Assembly Functions from the C Program To call functions from different language programs, the rules for calling functions from C and defining the called functions and variables using assembly are as follows. Assembly Language Program Function Definition Rules →→ Add an underscore character prior to the function name and declare it as a public variable (A-1) →→ If the function includes parameters, setup the corresponding variables in RAM bank0 and declare them as public variables (A-2) →→ If the function includes return values which are int/short data types, then declare the variable RH as an external variable. If the data types are long/float, then declare RH, RM and RU as external variables (A-3). →→ Set the corresponding variables, A, RH, RM or RU (A-4) before the function returns according to the data type of the return value. Rev. 1.30 137 March 15, 2011 Using Enhanced Holtek C Ex. EXTERN RH: byte ;; declare the high byte of the return value as an external value (A-3) PUBLIC_DISPLACE ;; add an underscore character to the beginning of the function name and declare it as public (A-1) PUBLIC DISPLACE0, DISPLACE1 ;; declare the parameters as public variables (A-2) RAMBANK 0 DISPDATA ;; define the RAM to save the parameters as RAM bank 0 (A-2) DISPDATA .section ‘data’ ;; (A-2) DISPLACE0 DB ? ;; define the first parameter row (char, 1 byte) (A-2) DISPLACE1 DB2 dup(?) ;; define the second parameter col (int, 2 byte) (A-2) RESULT DB ? ;; execution result, low byte of the temporary variable ;; define function _DISPLACE CODE .section ‘code’ _DISPLACE: CLR [0Ah].0 ;; clear the CF flag RLA DISPLACE0 ;; read the first parameter row, shift left one bit, save to A ADD A, DISPLACE1 ;; read the low byte of the second parameter col and add to A MOV RESULT, A ;; save the low byte of the result to the temporary variable MOV A, 0 ADC A, DISPLACE1[1] ;; add the high byte of the second parameter MOV RH, A ;; save the high byte of the return value to RH (A-4) MOV A, RESULT ;; save the low byte of the return value to A (A-4) RET ■ Calling Rule of C → Uses capital letters to define and declare the called function name (C-1) →→ Call the function (C-2) Example: extern int DISPLACE(char row, int col) ; // uses capital letter to define the function name (C-1) void main( ) { int disp ; // return value disp = DISPLACE(10, 20) ; // call the function (C-2) } In the above example, the return value is 40 after C calls DISPLAY(10, 20) 10.5 Calling C Function from Assembly Program ■ Calling Rule of C → Uses capital letters to define and declare the called function name (C-1) Example: int DISPLAY(char row, int col) ; // declare the function type (C-1) int DISPLAY(char row, int col) // define function (C-1) { int retval ; retval = (int)(row << 1) + col ; return retval ; } Rev. 1.30 138 March 15, 2011 Using Enhanced Holtek C ■ Assembly Language Program Function Definition Rules → Declare the function name starting with underscored letters to be external functions (A-1) → If the function includes parameters, declare all the corresponding variables to be external variables (A-2) → If the function includes return values whose data type is int/short, declare the variable RH to be an external variable. If the data type is long/float, declare RH, RM, and RU to be external variables (A-3) → Call the C function and read the return values form A, RH, RM or RU (A-4) Example: EXTERN RH:byte ;; declare the high byte of the return value as an external variable (A-3) EXTERN_DISPLACE: near ;; add underscored letters to function names and declare them to be external (A-1) EXTERN DISPLACE0: byte ;; declare parameters as external variables (A-2) EXTERN DISPLACE1: byte ;; declare parameters as external variables (A-2) ;; call functions _DISPLACE CODE .section ‘code’ Start : MOV A, 10h MOV DISPLACE0, A ;; save the value to the first parameter row MOV A, 20h MOV DISPLACE1, A ;; save to the col low byte of the second parameter CLR DISPLACE1[1] ;; set the high byte of the second parameter to 0 CALL _DISPLACE ;; call the C function _DISPLACE ;; the return value 40 of the C function will be saved in A, RH by 0 (A-4) …… RET__ Rev. 1.30 139 March 15, 2011 Using Enhanced Holtek C Holtek Semiconductor Inc. (Headquarters) No.3, Creation Rd. II, Science Park, Hsinchu, Taiwan Tel: 886-3-563-1999 Fax: 886-3-563-1189 http://www.holtek.com.tw Holtek Semiconductor Inc. (Taipei Sales Office) 4F-2, No. 3-2, YuanQu St., Nankang Software Park, Taipei 115, Taiwan Tel: 886-2-2655-7070 Fax: 886-2-2655-7373 Fax: 886-2-2655-7383 (International sales hotline) Holtek Semiconductor Inc. (Shenzhen Sales Office) 5F, Unit A, Productivity Building, No.5 Gaoxin M 2nd Road, Nanshan District, Shenzhen, China 518057 Tel: 86-755-8616-9908, 86-755-8616-9308 Fax: 86-755-8616-9722 Holtek Semiconductor (USA), Inc. (North America Sales Office) 46729 Fremont Blvd., Fremont, CA 94538, USA Tel: 1-510-252-9880 Fax: 1-510-252-9885 http://www.holtek.com Copyright© 2011 by HOLTEK SEMICONDUCTOR INC. The information appearing in this Data Sheet is believed to be accurate at the time of publication. However, Holtek assumes no responsibility arising from the use of the specifications described. The applications mentioned herein are used solely for the purpose of illustration and Holtek makes no warranty or representation that such applications will be suitable without further modification, nor recommends the use of its products for application that may present a risk to human life due to malfunction or otherwise. Holtek's products are not authorized for use as critical components in life support devices or systems. Holtek reserves the right to alter its products without prior notification. For the most up-to-date information, please visit our web site at http://www.holtek.com.tw. Rev. 1.30 140 March 15, 2011