Lab Manual for ECE

lic lab manual for ece communication system lab manual for ece free download
KomalMittal Profile Pic
Published Date:13-07-2017
Your Website URL(Optional)
Lab Manual for ECE 455 Spring, 2012 Department of Electrical and Computer Engineering University of Waterloo Written by Bernie Roehl ( Version 1.1 Last updated May 1, 2012 Copyright (c) 2011,2012 by Bernie Roehl. All rights reserved unless otherwise noted. NOTE: Students enrolled in ECE courses at the University of Waterloo are authorized to print or duplicate this lab manual in its entirety for their personal use. 1. Development Environment All development for the MCB1700 is done using the Keil μVision IDE. μVision is similar to many other IDEs you may be familiar with such as Eclipse or Visual Studio. It combines an editor, compiler, debugger and simulator into a single development environment. The μVision environment is very generic, and can work with a variety of different processors and boards. It can also build binaries to run in flash memory, static RAM or a simulator that’s included with the package. Depending on your lab, much of the initial development work can be done using the simulator. You can download the free version of µVision and use it with the simulator on your own computer, and only use the actual MCB1700 boards for final testing. The only significant limitation of the free version of the software is that the size of your binary is limited to 32 KB, which should be adequate for any of our labs. Setting Up Your System The μVision software is already installed on the computers in the lab. However, if you are doing development using your own computer, you will need to install μVision. You can get it from the following URL: When you install μVision, you should ask it to also install the example projects for the “Keil (NXP) MCB1xxx Boards”. You will also need to let it install the ULINK driver, which communicates with the Cortex debugger interface on the board using an attached daughterboard. The next step is to create a folder for your course, either on your own computer or on your Nexus P: drive (or your N: drive, if your P: drives is not available for any reason). You can call it whatever you like, but it should probably be named for the course (e.g. something like “ece455”). Once you’ve created the folder, download the support files from the following URL: Unzip that file into the folder you created. You are now ready to start creating projects for your course. A Quick Tour of the IDE The full documentation for the μVision IDE can be found at the following URL: That document is over 180 pages long, and most of it is very introductory in nature. It also covers multiple different processor types that are not used in our labs. You should not need to refer to that document. The IDE consists of a number of elements: (image courtesy of Keil) At first, it may seem very complex, but there are really only a few areas you need to concern yourself with: the Project windows, the Editor windows, the Output windows and the toolbars. (image courtesy of Keil) The Project window is generally used to give you a hierarchical view of the various files that are part of your project, and can also be used to browse functions, templates and documentation. The Editor window has a set of tabs, one for each file you have open. You edit your source code in this area, and the editor provides some limited syntax highlighting for your C code. The Output area gives you the results of your build operation, lists of errors, and other general information. There are three toolbars. The “File” toolbar is always present, and lets you access commonly- used features such as load, save, undo, redo, cut, paste, search and so on. It also has some frequently-used buttons for handling breakpoints. The “Build” toolbar is only present when you’re compiling code, and the “Debug” toolbar is only present when you’re debugging your application. Note that you will always be in one of those two modes (Build or Debug). To switch between the two, click on the Debug button in the File toolbar ( ). All the icons in the toolbars have tooltips that appear if you hover over the icon, and the meaning of each should be clear (e.g. “set breakpoint”). The individual windows can be dragged around by their title bars, and you can tile the editor windows to examine multiple source files at the same time. If things get out of hand, you can use the Window menu item “Reset view to defaults” to get back to a reasonable window layout. The Debug tools will be discussed later, after we’ve examined the hardware. For now we’ll focus on the Build process. Creating a New Project Launch μVision, and use the menu item Project/New μVision Project to create a new project. Choose the folder you created for your course, and give your project a name (e.g. “lab1”). You will then be asked to specify what hardware you’ll be using. Expand the “NXP (founded by Philips)” tree and select the “LPC 1768” processor. When asked if the startup files should be copied over and added to your project, choose “yes”. In the Project window, click on “Target 1” (in the hierarchy tree), then click it again so you can rename it to something more meaningful such as “LPC1768 Flash”. Expand the target, then right click on “Source Group 1” and select “Add files to group Source Group 1”. In the resulting dialog box, under “Files of type:”, choose “All files”. Select "system_LPC17xx" and "GLCD_SPI_LPC1700" (both are C source files) and “GLCD” (a header file). Click Add, and then Close. What you’ve done is create a new project with a new build target (“LPC1768 Flash”), then added the system startup code, the display routines and the display header file to the project. At this point, you’re ready to start writing your code. Creating Source Files Using the “File/New” menu item, create a new file. It will initially be a “.txt” file, so use “File/Save as” to save it out with a “.c” extension (e.g. “main.c”). Right click on “Source Group 1” in the Project window, select “Add files to group Source Group 1”, select your source file, then click Add and Close. You must do this once for each new source file in your project. Forgetting to do this is a very common mistake. Note that you can rename “Source Group 1” to something more meaningful. You can also rename your target, create additional targets, and so on by right-clicking on the target in the Project window and selecting “Manage components”. Building and Running the “Hello world” example Here is the code for a simple “Hello, world” example: include lpc17xx.h include "glcd.h" int main(void) SystemInit(); GLCD_Init(); GLCD_Clear(White); GLCD_DisplayString(0, 0, 1, "Hello, world"); return 0; The first line brings in the definitions for the various registers that are on the LPC1768 family of processors. The second line brings in the definitions for the display routines that put text and graphics on the LCD panel. Obviously the glcd.h file is only needed if you’re using the display. The very first thing we do in main() is call SystemInit(), which does the initialization for us. If you open the system_17xx.c file by double-clicking on it in the Project window, then look at the bottom of the editor window, you should see a “configuration wizard” tab. This wizard allows you to configure your clock settings as well as the power settings for the on-chip peripherals. The SystemInit() function is where all that initialization takes place. These settings wil be discussed in detail in the Hardware section of this manual. The GLCD_Init() function does the tedious work of initializing the LCD display. The next line calls the GLCD_DisplayString() function to put a character string on the display at line 0, row 0, using font 1 (the larger of the two available fonts). Build your application by hitting F7. If there are no errors, click on the LOAD button( ) to download the code into flash memory on the MCB1700 board. To run it, just hit the button marked “Reset” on the board. From within the IDE you can debug your program by single-stepping and setting breakpoints. You can also simulate the execution of your software without needing to have access to the board itself. Both of these will be discussed later in this manual, but first we need to become more familiar with the hardware on the board. 2. Hardware Overview The board being used in our labs is the Keil MCB1700. It uses the NXP LPC 1768 processor, consisting of an ARM core (specifically, the Cortex M3), 512 KB of flash memory and 64 KB of SRAM along with a wide variety of on-chip peripherals. The board itself is relatively simple, and aside from the LPC 1768 itself there are only a few support circuits (mostly RS-232 level converters, ethernet transceivers, audio amplifiers and so on). The board provides a USB port, two serial (COM) ports, two CAN ports, an ethernet connector, a micro SD card slot, a potentiometer, a speaker and a set of LEDs and buttons. It also provides a full-colour LCD display. (image courtesy of Keil) In our department labs, the board is connected to the host computer using two USB cables. One simply provides power to the MCB1700, while the other connects to a ULINK-ME daughterboard that is plugged into the Cortex debugging interface on the MCB1700. It is through the ULINK-ME that you will be downloading your compiled code and debugging it. Since all the peripherals are on-chip, your primary reference for the board will be the manual for the LPC 1768 chip itself. This can be found at the following URL: Note that the document is 840 pages long, and only a small fraction of it is relevant to your labs, so there is no need to print out the entire document. This lab manual will provide all the information you need, and will also reference specific sections of the product document which you can either refer to online or print out if necessary. Major Functional Blocks The LPC 1768 is a “system on a chip” that combines flash, SRAM, timers, a DAC, a multi- channel ADC and numerous communications interfaces onto a single IC. Note that many of the blocks identified in this next diagram are not used in our labs. In this manual we will only be discussing those blocks that you will actually be using. (image courtesy of Keil) Memory There are four different blocks of memory on the LPC 1768. There is a block of 512 KB of flash memory, located at the bottom of the address space, which is used for storing your code and data. There is an 8 KB boot ROM, which is hard-coded and unchangeable. There is a block of 32 KB of static RAM for use by the application (it’s also possible to use this space for code, as an alternative to using the flash memory). And finally, there are two banks of 16 KB of static RAM that are shared with peripheral devices. All the peripherals are memory-mapped, so they are accessible directly from C. The overall system memory map is as follows: 0000 0000 to 0008 0000 512 KB of flash 1000 0000 to 1000 8000 32 KB of SRAM 1FFF 0000 to 1FFF 2000 8 KB of boot ROM 2007 C000 to 2008 4000 two 16 KB blocks of shared SRAM 2009 C000 to 200A 0000 GPIO 4000 0000 to 4008 0000 APB0 peripherals 4008 0000 to 4010 0000 APB1 peripherals 5000 0000 to 5020 0000 AHB peripherals E000 0000 to E00F FFFF Cortex-M3 related registers Note that the address space is sparse, so the above blocks are not consecutive and are separated by “reserved” areas. The three peripheral blocks allocate 16 KB to each peripheral. The AHB block contains the ethernet controller, USB controller, and DMA controller. The APB blocks contain all the other peripherals. Note that the pin connect registers (described below) are considered a “peripheral” in this context. More detailed memory maps can be found in section 2 of the LPC 1768 documentation. You generally will not need to deal with hardware addresses directly, since the lpc17xx.h include file contains definitions for all the registers. This is done using a set of variables that each reference a group of registers as a struct, and the individual registers as fields within that struct. This will be explained in detail in each of the sections below. Pin Connect Block Because of the large number of internal peripherals, most of the pins on the chip can perform as many as four different functions. It is therefore necessary for you to specify what function you want each pin to be used for. This is accomplished by programming a set of registers known as the Pin Connect Block. For example, pin P0.0 can be used for RD1 (received data on CAN port 2 1), TXD3 (transmit data on UART 3), SDA1 (part of the I C interface) or as a general-purpose input/output pin, depending on the setting of a pin select register. The specifics of selecting particular pin functions will be discussed as needed in the sections below. You may also need to set the mode of each pin (internal pull-up resistor, pull-down resistor, or neither). The pin connect block is referenced from C as a struct called LPC_PINCON, with fields called PINSEL1, PINSEL2, PINMODE1, PINMODE2, and so on. Power Control Block Because the LPC 1768 is designed to be used in mobile devices, power consumption is a key issue. There is a set of registers that allow an application to enable and disable power to the main processor as well as each individual on-chip peripheral. By powering down peripherals that are not used in a particular application, considerable power savings can be achieved. For our labs, we power the board from the host computer and so power consumption is not an issue. However, some peripherals default to being “off”, so you will need to use the power control block to power them up. The specifics of doing this will be addressed in the sections below. The 32-bit peripheral power control register is referenced from C as LPC_SC-PCONP. LPC_SC is a general system-control register block, and PCONP referes to Power CONtrol for Peripherals. Note that you can also specify which peripherals should be powered on using the configuration wizard, described elsewhere in this document. Vectored Interrupt Controller All the internal peripherals are capable of generating interrupts. The specific conditions that produce interrupts can be set individually for each peripheral, and the individual interrupts can be enabled or disabled using a set of registers. Writing an interrupt handler in C is very easy just create a function with an appropriate name and it will automatically be used. The name consists of the prefix from the table below, with “Handler” appended (e.g. TIMER0_IRQHandler). Int. number Prefix Description 0 Watchdog timer (not used by our labs) WDT_IRQ 1 Timer 0 TIMER0_IRQ 2 Timer 1 TIMER1_IRQ 3 Timer 2 TIMER2_IRQ 4 Timer 3 TIMER3_IRQ 5 UART 0 UART0_IRQ 6 UART 1 UART1_IRQ 7 UART 2 UART2_IRQ 8 UART 3 UART3_IRQ 9 PWM 1 (unused on MCB1700) PWM1_IRQ 2 10 I C 0 (unused on MCB1700) I2C0_IRQ 2 11 I C 1 (unused on MCB1700) I2C1_IRQ 2 12 I C 2 (unused on MCB1700) I2C2_IRQ 13 SPI (used for communicating with LCD display) SPI_IRQ 14 SSP 0 (unused on MCB1700) SSP0_IRQ 15 SSP 1 (unused on MCB1700) SSP1_IRQ 16 PLL 0 (interrupts not used by our labs) PLL0_IRQ 17 Real-time clock RTC_IRQ 18 External interrupt 0 EINT0_IRQ 19 External interrupt 1 (unused on MCB1700) EINT1_IRQ 20 External interrupt 2 (unused on MCB1700) EINT2_IRQ 21 External interrupt 3 (unused on MCB1700) & GPIO interrupt EINT3_IRQ 22 ADC end of conversion ADC_IRQ 23 Brown-out detected (not used in our labs) BOD_IRQ 24 USB USB_IRQ 25 CAN CAN_IRQ 26 DMA DMA_IRQ 2 27 I S (unused on MCB1700) I2S_IRQ 28 Ethernet ENET_IRQ 29 Repetitive-interrupt timer RIT_IRQ 30 Motor control PWM MCPWM_IRQ 31 Quadrature encoder QEI_IRQ 32 USB phase-locked loop (interrupt not used in our labs) PLL1_IRQ 33 USB activity USBActivity_IRQ 34 CAN activity CANActivity_IRQ Note that a particular peripheral can generate its interrupt for a variety of reasons, which are configured within that peripheral. For example, timers can be configured to generate interrupts either on match or on capture. By default, the interrupt vector table is at the very bottom of physical memory (address 0000 0000). However, you can change this location by setting the Vector Table Offset Register (SCB-VTOR, section 6.4), placing the table anywhere in the lowest 1 GB of address space. For alignment reasons, it must be located on a 1024 byte boundary. In practice, you will typically only relocate it to the bottom of SRAM at 1000 0000. The priorities of the interrupts can be set individually. See sections 6.5.11 to 6.5.19 for details. A set of functions is available for enabling and disabling specific interrupts, setting their priority, and controlling their pending status: void NVIC_EnableIRQ(IRQn_Type IRQn) void NVIC_DisableIRQ(IRQn_Type IRQn) void NVIC_SetPriority(IRQn_Type IRQn, int32_t priority) uint32_t NVIC_GetPriority(IRQn_Type IRQn) void NVIC_SetPendingIRQ(IRQn_Type IRQn) void NVIC_ClearPendingIRQ(IRQn_Type IRQn) IRQn_Type NVIC_GetPendingIRQ(IRQn_Type IRQn) The IRQn names are just the prefix from the table above with an “n” appended (e.g. TIME0_IRQn). You can also enable or disable interrupts altogether using __disable_irq() and __enable_irq(); You can also trigger any interrupt in software by writing the interrupt number to the NVIC-STIR register (values up to 111 are permitted). You must clear interrupt conditions in the interrupt handler. This is done in different ways, depending on what caused the interrupt. For example, if you have INT0 configured to generate an interrupt, you would clear it by setting the low-order bit of the LPC_SC-EXTINT register. Interrupt handling is described in detail in section 6 of the Keil documentation. General-Purpose I/O Among the various functions that can be programmed using the pin control block is GPIO, or general-purpose I/O. GPIO allows individual pins to be used for input or output. In the case of the MCB1700, the board provides a set of eight LEDs which are connected to specific pins on the processor. It also provides a “joystick” consisting of four directional buttons and a center “select” button, as well as a single “INT0” button, all of which are connected to input pins on the processor. When the processor powers up or resets, the pins that are connected to the LEDs and buttons are automatically configured as GPIO (General-Purpose I/O). However, it is good practice to configure these pins before using them. In any case, it is also necessary to set the data direction register to indicate which pins are to be used for input (the buttons) and which for output (the LEDs). The GPIO control registers are accessed using structs called LPC_GPIO1 and LPC_GPIO2. Configuring the INT0 button for input is done like this: LPC_PINCON-PINSEL4 &= (320); // P2.10 is GPIO LPC_GPIO2-FIODIR &= (110); // P2.10 is input The first line above enables the INT0 button to be used for input by setting bits 21 and 20 of the PINSEL4 register (section 8.5.5) to zero, which corresponds to GPIO. The second line sets bit 10 in the GPIO2 I/O direction register (section 9.5.1) to zero, meaning the pin will be used for input. In theory you could omit these two lines, since zero is the default value for both of these registers. Setting up the “joystick” is done as follows: LPC_PINCON-PINSEL3 &= ((3 8)(314)(316)(318)(320)); / P1.20, P1.23..26 is GPIO (Joystick) / LPC_GPIO1-FIODIR &= ((120)(123)(124)(125)(126)); / P1.20, P1.23..26 is input / The first line of code sets five specific bits in the LPC_PCON-PINSEL3 register (section 8.5.4) to zero, again meaning that they will be used for general purpose I/O. The second line clears bits in the I/O direction register, indicating that the five buttons of the “joystick” are to be used for input. Again, these steps could be omitted since zero is the default value. Configuring the LEDs for output is straightforward: LPC_GPIO1-FIODIR = 0xB0000000; // LEDs on PORT1 LPC_GPIO2-FIODIR = 0x0000007C; // LEDs on PORT2 These two lines simply set the pins corresponding to the LEDs to be output by turning on the corresponding bits in the I/O direction register (section 9.5.1). Notice that in this case we didn’t bother explicitly setting the pins to be GPIO. We didn’t need to do that in the earlier code either, since GPIO is the default setting. To turn on a particular LED, you would use the following code: const U8 led_pos8 = 28, 29, 31, 2, 3, 4, 5, 6 ; mask = 1 led_posled; if (led 3) LPC_GPIO1-FIOSET = mask; else LPC_GPIO2-FIOSET = mask; Notice that the LEDs are split over two separate GPIO ports, with the first three on GPIO port 1 and the remaining five on GPIO port 2. See section 9.5.2 for details. To turn the LEDs off, write to the FIOCLR registers rather than FIOSET. All the joystick buttons are on GPIO port 1 (section 9.5.4), so to read the “joystick” you would simply do this: kbd_val = (LPC_GPIO1-FIOPIN 20) & 0x79; The INT0 button is on GPIO2, so reading it is just: int0_val = (LPC_GPIO2-FIOPIN 10) & 0x01; You can enable interrupts from any pin on port zero or port 2, triggered on the rising and/or falling edge. This is enabled using registered in the LPC_GPIOINT block called IO0IntEnF, IO0IntEnR, IO2IntEnF and IO2IntEnR (the ‘F’ stands for falling edge, and the ‘R’ stands for rising edge). The GPIO shares the vector for External Interrupt 3, so that’s what you should enable. For example, the following code enables an interrupt on the falling edge of pin P2.10 (which happens to be the INT0 button on the board): LPC_GPIOINT-IO2IntEnF = 1 10; // falling edge of P2.10 NVIC_EnableIRQ(EINT3_IRQn); In your interrupt handler you should clear the interrupt condition by writing 1 bits to the appropriate IntClr register. In this particular case you would do the following: LPC_GPIOINT-IO2IntClr = 1 10; // clear interrupt condition Clocks The clock system on the LPC 1768 is extremely flexible. The following diagram gives an overview of how the pieces fit together: (image courtesy of Keil) You can ignore anything related to the USB clock, since we will not be using it in our labs. The same is true of the watchdog timer. You can select any of three different clock sources by setting the bottom two bits of the LPC_SC-CLKSRCSEL register (section 4.4.1) as follows: 00 internal 4 MHz RC oscillator (this is the default) 01 12 MHz external oscillator 10 32 KHz realtime clock oscillator Regardless of which clock source is used, it can either be used directly or as the input to a phase-locked loop that scales it up to a higher frequency (that still follows the phase of the input clock) and then divides it down to a lower frequency. There are many configuration options for the PLL, which are beyond the scope of this lab manual but which are described in section 4.5 of the LPC 1768 documentation. The input clock (or the output of the PLL, if used) is then put through by a divider whose value is stored is bits 7:0 of the LPC_SC-CCLKCFG register (see section 4.7.1). If the PLL is used, the divisor value in CCLKCFG must be at least 2. The output from that divider is the actual CCLK signal to the processor. That processor clock signal is further subdivided by 2, 4 and 8 to provide four separate peripheral clocks (including the original, undivided processor clock signal), and each peripheral device on the chip can be configured to use any of those four clocks for its operation (see section 4.7.3 for details on which bits in the LPC_SC-PCLKSEL0 and LPC_SC-PCLKSEL1 registers control the clock selection for each peripheral). Configuration Wizard Setting all these values, particularly those of the PLL, is a complex operation that involves (among other things) waiting for the PLL to lock onto the incoming frequency. Fortunately, much of this complexity is handled by the startup code that is included with the development kit. You do not even have to modify that startup code by hand, since the μVision IDE (described earlier) provides a configuration wizard that handles the updating of the startup code. All you need to do is choose appropriate settings. Double-click the system_17xx.c file in the Project window to open it in the editor, then click on the “Configuration Wizard” tab below the file in the editor window. Most of the settings are straightforward. Since the MCB1700 is powered from the host computer, there is no reason not to use the main (external) oscillator. Therefore the oscillator enable (OSCEN) bit should be set. The OSCRANGE should be set to “1 MHz to 20 MHz” to match the 12 MHz crystal on the board. The PLL should be configured to use the oscillator as its input. The main parameters that should be set for PLL0 are M and N, the multiplier and divisor. The output frequency will be 2MF/ N, where F is the input frequency. When using the oscillator, M should not be less than 6 or greater than 512, and N should always be in the range 1 to 32. In the example shown above, M is set to 100 and N is set to 6. Therefore, the 12 MHz oscillator will produce an output frequency from the PLL of 2 100 12 / 6 = 400 MHz. Since the CCLKSEL value is 4 (which is set into the bottom 8 bits of the LPC_SC-CCLKCFG register during system initialization), the CCLK will be 100 MHz (i.e. 400 MHz divided by 4). The four peripheral clocks would be 100 MHz, 50 MHz, 25 MHz and 12.5 MHz, and you can configure any peripheral to use any of these four clocks (using the two Peripheral Clock