This page last updated on Mon, 08 Mar 2010 19:55:43 -0600 ----------------------------------------------------------------------------------------------- A FREQUENCY COUNTER PROJECT - Part-3 (Making it all work together) Dieter (Diz) Gentzow -- W8DIZ w8diz@tampabay.rr.com ----------------------------------------------------------------------------------------------- PREFACE: Part-3 of the Frequency Counter project will bring together all the things we learned in Parts 1 and 2. For Part-3, you will need a built prototype of the freq counter, either from your own parts or purchase the Freq Counter Development Kit offered at the end of this article. You should also have reviewed the internals of the ATmel AVR microprocessor, part number ATtiny2313-20, and the articles referenced in the addendum files noted at the end of this article. We will write assembly code on a Linux or Windows PC, assemble the code, download the code to the target prototype hardware and hopefully get the target hardware to function as intended. If you do not have access to the first two articles of this project, you may download them from the links in the notes section of this article. All references to computers will be for the Linux Mint operating system, but conversion to Windows is easy. Any reference to the GEDIT text editor can be read as NOTEPAD or your favorite TEXT editor for Windows. The AVRA assembler and AVRDUDE are both available for Linux and Windows. OK, here we go: THE AVRA ASSEMBLER: If you have not installed the AVRA assembler on your PC, then you need to install it now. For Windows, go to http://sourceforge.net/projects/avra/files/ and download and install version 1.2.3 filename avra-1.2.3-win32.zip and for Linux Mint, follow the installation proceedure listed in the addendum at http://w8diz.com/qq-fc-project/part-1/ Now that you have AVRA installed, let's test the program. Create a directory on your Desktop called qq-fc-project by right-clicking the Desktop and selecting "Create Folder". Inside this new directory, create a sub-directory (Folder) called led-code. Start GEDIT and save the file as led.asm in the led-code subdirectory. Enter the follow code/text exactly as shown using GEDIT: -------------------------------------------------------- ;led.asm ;cd Desktop/qq-fc-project/led-code/ .include "tn2313def.inc" nop -------------------------------------------------------- Save the program by holding down the ALT key and typing "F", then release the keys and type "S". The first 2 lines of the code are a comment lines, indicated by the starting semicolon. The third line tells the AVRA assembler to insert(include) a file into the source code. The fourth line is the OP-CODE for "no operation" which is usually used as a place holder or to literally waste CPU time. Now we are ready to assemble the code. For Linux users, open the TERMINAL program by clicking on Menu and then Terminal under the System column. When the Terminal program pops up on the screen (in Linux), it is running from your default directory, a sub-directory of the /home directory. Let's cut and paste the command in line 2 of your source code into the Terminal program. Highlight "cd Desktop/qq-fc-project/led-code/" with your mouse; hold down the CONTROL key and hit the "C" key to copy the line of code into your "buffer". Now paste the command into the Terminal program by holding down the CONTROL & SHIFT keys and type "V". This should make the terminal command line look like: ---------------------------------------------------------------- username@username-desktop ~ $ cd Desktop/qq-fc-project/led-code/ ---------------------------------------------------------------- Now hit the enter key to execute the command. The new command line should look like: -------------------------------------------------------- username@username-desktop ~/Desktop/qq-fc-project/led-code $ -------------------------------------------------------- Now we are ready to assemble the source code into usable "machine code" for the target hardware. At the new command line, type avra led.asm and hit the Enter key. Note the avra program name must be entered in lower-case. If all went well, your source code should have compiled with "no errors" and "1 warnings". The warning is telling you that the avra program could not find the ".DEVICE definition" which is part of the ATtiny2313def.inc file. To fix this, download the include file and place the file into your led-code subdirectory. Instruction are in the addendum. Let's compile the source code again by executing the last command in the terminal program; hit the UP-Arrow on your keyboard. This should display the last command on the Terminal command line; now hit the Enter key. With the include file placed into the led-code sub-directory, the source code should compile with no errors and no warnings. So what did we accomplish? Not much except that we verified the assembler is working and that we are now ready to write some serious code. WRITING CODE TO MAKE AN LED BLINK EXACTLY ONCE PER SECOND: We are now going to "learn by doing" a simple example that employs the most important functions of the ARV microcontroller, the INTERRUPTS. From Wikipedia: "In computing, an interrupt is an asynchronous signal indicating the need for attention or a synchronous event in software indicating the need for a change in execution. A hardware interrupt causes the processor to save its state of execution and begin execution of an interrupt handler." In order to make an LED blink for an exact time period, we must first have an accurate clock source. Our project uses a 20.48 MHz crystal that can be trimmed to oscillate at exactly 24,480,000 Hertz. The 24.48 MHz oscillator signal is used as the heartbeat of the ATtiny2313 CPU. Inside the CPU is a divide by 1024 circuit that can be activated through software instructions that produces a 50 uSecond time period. Also inside the CPU is an 8 bit timer overflo interrupt that we activate by initializing(presetting) the 8 bit timer to (256-200) or 56. After the timer counts up to 256 (a total of 200 times 50 uS), it triggers the timer0 overflow interrupt, thus the timer0 overflow interrupt is activated every 10 milliseconds. Now all we need to do is to turn on the LED after we count to 900 mS and turn off the LED after another 100 mS, giving us a 10 LED percent duty cycle. Note that the firmware included in the FC Kit causes the LED to bling ON for 1 sec and OFF for 1 sec. The code below was changed after the chips were programmed. Download the led2.asm code into the led-code sub-directory per instructions in the addendum. Here is the code with extensive comments: ----------------------------------------------- ;led2.asm - demo program to blink an LED once every second ;open Terminal and move to active directory : cd Desktop/qq-fc-project/led-code/ ;to assemble code in the directory that holds led2.asm : avra led2.asm ;to write fuse bits : avrdude -P usb -p t2313 -c avrispv2 -U lfuse:w:0xFF:m ;to upload code to the target : avrdude -P usb -p t2313 -c avrispv2 -U led2.hex .include "ATtiny2313def.inc" ; include file that defines the variables in ALL CAPS .equ Led = PIND4 ; The Led is connected to PORTD PIN4 .equ LedMask = 0b00010000 ; Bit 4 ($10) mask for the Led .def temp1 =r16 ; work register .def temp2 =r17 ; work register .def delay =r18 ; register that holds the timeout delay value .cseg ;following is code and goes into ROM space .org $000 ; this is the start of the Interrupt Vector table rjmp Reset ; on power-up, program starts to execute at location $000 reti ; IRQ0 - not used reti ; IRQ1 - not used reti ; Timer1 Capture - not used reti ; Timer1 Compare - not used reti ; Timer1 Overflow - not used rjmp Tim0_Ovf ; Timer0 Overflow Interrupt Vector reti ; UART Receive - not used reti ; UART empty - not used reti ; UART Transmit - not used reti ; Analog comparator - not used reti ; Pin Change - not used reti ; Timer1 Compare B - not used reti ; Timer0 Compare A - not used reti ; Timer0 Compare B - not used reti ; USI Start - not used reti ; USI Overflow - not used reti ; EEPROM Ready - not used reti ; Watchdog Overflow - not used Reset: ;Reset Interrupt Vector points here. ldi temp1,low(RAMEND) out SPL,temp1 ; Set stack pointer to last RAM loc ldi temp1,high(RAMEND) out SPH,temp1 ; Set stack pointer, 12 bits wide ldi temp1,LedMask out DDRD,temp1 ;make the Led pin an output ldi temp1,0b00000101 ;set timer0 prescaler to 1024 out TCCR0,temp1 ;using 20.48 XTAL = 50uS ldi temp1,0b00000010 out TIMSK,temp1 ;enable TIMER0 overflow interrupts ldi delay,10 ; init the delay to 100 mSec sei ;enable all Interrupts (global interrupt enable) Menu: ; main program - loops here forever rjmp Menu Tim0_Ovf: ldi temp1,256-200 ;10 ms using a 20.48 xtal out TCNT0,temp1 ;set for next overflow dec delay ; decrement the delay counter brne Tim0_Ovf_Exit ; if it is NOT ZERO, then exit the interrupt routine in temp1,PORTD ; get the current Led Port Value ldi temp2,LedMask ; load the Led bit eor temp1,temp2 ; exclusive or the Led bitmask with the PORTD value brne Tim0_Ovf_1 ; if it is NOT ZERO, then branch and turn off Led ldi delay,10 ; reset the delay counter to 100 mSec cbi PORTD,Led ; turn Led on rjmp Tim0_Ovf_Exit ; exit the Interrupt routine Tim0_Ovf_1: ldi delay,90 ; reset the delay counter to 900 mSec sbi PORTD,Led ; turn Led off Tim0_Ovf_Exit: reti ; return from Interrupt ------------------------------------------------ To reiterate the above code in plain English: First we include the source file that contains many definitions. Next, we define the labels and register variables that we will use. Then we fill in the Interrupt Vector table. followed by the main code starting with "Reset:" where we place the stack pointer at the top of static memory, aka RAM. We then enable one of the I/O lines (PORTD Pin-4) to be an output pin to control the LED. Next we set the prescaler to divide the 20.48 MHz clock by 1024 to produce 50 uSec timer counts in the CPU. We then enable the TIMER0 overflow interrupt and then initialize the delay timer to 100 mSec and then we enable the global interrupt flag as a master interrupt enable switch for the system. After that, the CPU runs in a tight loop doing a relative jump to "Menu:" forever. The first time that the TIMER0 overflow interrupt is activated, the "Tim0_Ovf:" interrupt routine sets the timeout for the next interrupt by loading the "TCNT0" register with 56. When TIMER0, an 8 bit timer, counts up to 256 (every 50 uSec.), we get another interrupt. Next in the interrupt code, we decrement the "delay" variable register. If the register has counter down to zero (0) then we need to test the status of the LED and determine if we need to turn it on or off, else we exit the interrup routine. The Led OFF time and the Led ON time are both independently controlled. UPLOADING THE LED CODE TO THE TARGET HARDWARE: You may disconnect the LCD from the target hardware for this demonstration; the LCD is not used. To upload the machine code file (led2.hex) to the target hardware, we need interface hardware and software. The hardware recommended is the AVRISP-II referenced in Addendum Part-1. There is also a software fix pertaining to the AVRISP in the Addendum Part-2. See the reference at the end of this document under notes. To upload the machine code to the target, we also need a "PROGRAMMER" or software that talks between the AVRISP and the PC, We will be using the free program called AVRDUDE, referenced in Addendum Part-1. Connect the AVRISP module between a PC USB port and the 6 pin header on the target hardware. Make sure that the 6 pin header connection has the red wire toward the edge of the target PCB or away from the CPU. Start the Terminal program and move to the working directory by running the command: "cd Desktop/qq-fc-project/led-code/" Make sure the response is "username@username-desktop ~/Desktop/qq-fc-project/led-code $", verifying that you are in the correct directory. Next, apply power to the target hardware. Then issue the commmand: "avrdude -P usb -p t2313 -c avrispv2 -U led2.hex" and take a deep breath. If all is working correctly, this should upload the led2.hex file machine code to the target hardware and start executing the code, once loaded. The Led should start to blink, ON for 100 mSec and OFF for 900 mSec. If you can not get the hardware to blink the Led, review the preceeding instruction and find the fault. Should you get stuck, email me me. My email is listed at the end of this article. OK...congratulations are in order (I think), assuming that you managed to control the Led. You may wish to "play" with the code and try different ON/OFF times for the Led. UPLOADING THE FREQUENCY COUNTER CODE TO THE TARGET HARDWARE: Connected the LCD to the Freq Counter PCB (target hardware) for this exercise. If you have not done so, download the frequency counter source code "fc.asm" per instructions in Addendum Part-3. Place the source code into a directory (folder) called "fc-code" under the "qq-fc-project" folder on your Desktop. In a new Terminal window, execute "cd Desktop/qq-fc-project/fc-code/" and compile the source code by running the avra program in a Terminal window, "avra fc.asm". Apply power to the target board and then upload the machine file to the target by executing "avrdude -P usb -p t2313 -c avrispv2 -U fc.hex". Your display should look something like this:Pix source http://w8diz.com/qq-fc-project/part-3/fc-working.jpg FIRMWARE/HARWARE REVIEW: Open the source file fc.asm in GEDIT as a reference to the following review. To best follow this discussion, you should enable the line numbers in GEDIT. In GEDIT, click on "Edit", "Preferences" and enable Line Numbers. then Close the Preferences window. The Freq Counter accuracy display resolution is dependent upon two parameters: The gate time and the prescaler. The prescaler is set in hardware by selecting the desired jumper on the 74HC4040 binary counter. For this project, we selected divide by 4. Since the maximum frequency that the basic CPU can measure is about 9 MHz, the divide by 4 circuit enables us to measure up to about 36 MHz. If we set the gate time (measurement period) to one second, then frequency measurements would have a four hertz display resolution, because when the CPU counts the pulses for one second, we need to multiply the count by 4 to display the correct frequency. A gate time of one second is a bit long for most frequency counter applications so we use a gate time of one half second. This results in doubling our display resolution to 8 Hertz because we need to multiply the count result by 2. The gate time and prescaler could be controlled with jumpers of switches, but for simplicity we will stick to a prescale of divide by 4 and a gate time of one half second. The ASM code from lines 48 thru 66 define the FC gate time and prescaler, hence the display resolution. Lines 68-71 define if you want a frequency offset, but this code is not fully implemented. There will be an update in the Addendum Part-3. The rest of the code is split between frequency counting and the LCD display. The LCD can be controlled using either 4 data bits or 8 data bits. We use 4 data bits, so every command or character sent to the display requires two writes to the LCD, one nibble (4 bits) at a time. Before we send data to the LCD, we read the BUSY bit from the LCD. This is the fasted way to control the LCD. Many LCD interfaces do not read the BUSY bit, but rely on worst case timing requirements to control the LCD. Frequency counting is controlled by the Timer-0 interrupt routine (TIM0_OVF:) at line 476. The timer is configured to interrupt every 5 milliseconds and accumulates the number of pulses counted on PIN-9 (PD5) of the CPU. The 16 bit count is captured in lines 499 & 500 and added to accumulator registers in lines 502 to 525. After 100 interrupts (100 * 5 mSec or half a second) the result is converted to human readable characters (lines 537-580) and displayed on the LCD (lines 354-418). Line 599 is where you can include your callsign. Change "URCALL" to your callsign. Make sure that you have exactly 16 characters inside the quotes, else you may have some display problems. All data is broken into 16 bit boundaries so we need an even number of characters for the text display storage. Note the zeros at the end of the text line. Zero is interpreted as the text string terminating character. NEXT ARTICLE: Designing and Producing the PCB - Printed Circuit Board ABOUT THE AUTHOR: Dieter (Diz) Gentzow, W8DIZ, aka WB8QYY before April 2000, has been a licensed ham since 1973. Past employment include Honeywell as an Industrial Sales & Systems Engineer, AC Nielson, Electrical Engineer designing black boxes that monitor TV viewing habits and a handful of other hardware and/or software jobs. Currently semi-retired, living in sunny Palm Harbor, Florida. You can contact Diz, W8DIZ via eMail at w8diz@tampabay.rr.com and SKYPE ID = "w8diz_". NOTES: Addendums and/or corrections to this project are available at http://w8diz.com/qq-fc-project/ Frequency Counter Development Kits are available from http://kitsandparts.com Part-1 of this project is available at http://w8diz.com/qq-fc-project/part-1/part-1.php Part-2 of this project is available at http://w8diz.com/qq-fc-project/part-2/part-2.php