User Tools

Site Tools


old_lab_5

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

old_lab_5 [2014/10/15 12:51] (current)
hutch created
Line 1: Line 1:
 +====== Lab 5: Keypad & Seven Segment Display ======
 +===== Overview =====
 +In this lab you will write code for two I/O devices that require time-multiplexing:​ a keypad and a seven segment display.
  
 +===== Objective =====
 +Practice the concepts taught in Chapter 6 to implement a program that uses a keypad and a seven-segment display.
 +
 +===== Specifications =====
 +Use the principles taught in chapter 6 to create an embedded system that properly interacts with inputs and outputs. The system will receive input from a keypad and control the output on a seven segment display. Write a program that checks for pressed buttons on the keypad and displays the six most recently pressed buttons on the seven segment display. ​
 +
 +When a button is pressed its value should be shifted into the seven segment display'​s rightmost digit. For example, if I press 1 -> A -> 2 -> B -> 3 -> C in that order then the seven segment display should show: 1A2B3C. You may decide what to show for the * and # keys, but it should be distinct from what you show for other keys. Initially, the seven segment display should be all zeros.
 +
 +Respond to each button press only once. This means if the user holds down a button your program should only add it to the seven segment display once. The user must release the button and press it again to get another response.
 +
 +The keypad can have a lot of bounces and glitches. Filter the input by requiring a button press be detected for two consecutive samples, as described in section 6.3 of the book. You will have to figure out the timing so that quick button presses are not missed and no incorrect results are displayed due to glitches.
 +
 +The example code in the book for ScanKeypad() is inefficient. It writes and reads from the same column multiple times. In your keypad scanning code, ground each column and read the resulting value just once. This reduces AXI bus traffic so that instead of 16 writes and reads there are only 4. You are not required to use the columns as your outputs but the same principles apply if you choose to use the rows.
 +
 +Your code must be able to handle multiple button presses (up to 4). For example, if the user simultaneously presses two buttons your software should be capable of recognizing both of them and then displaying the two simultaneous key pushes to the display (in any order) . The ScanKeypad() example in the book did not do this (because it returns as soon as it finds a pressed key). Note that you will need to use the XGPIO_Set_DataDirection() function so that you dynamically configure only one output at a time during scanning. See the skeleton code below; in that example, 4 outputs are configured at a time (you will want to only configure one output at a time in your scanner state machine).
 +
 +===== Notes =====
 +
 +The keypad and seven segment display are connected to GPIOs just like the switches, LEDs, and buttons you have used in the past. [[zedboard_pin_map|More information can be found here]] such as which bit of the GPIO register controls which signal.
 +
 +The keypad works just like the one described near the end of chapter 6. The keypad has 16 buttons but only 8 pins that connect to the FPGA. 4 of the pins are connected to the columns on the keypad and the other 4 are connected to the rows. You should use 4 of the pins as inputs and 4 of them as outputs, and scan through the buttons as the book demonstrates. Your software interacts with the keypad via a GPIO named XPAR_KEYPAD_8BITS in the xparameters.h file. Thus, you must configure 4 bits of this GPIO to act as inputs and 4 to act as outputs. This is slightly different than the book where the inputs and outputs of the keypad were separated into A and B.
 +
 +[[http://​en.wikipedia.org/​wiki/​Seven-segment_display|The seven segment display]] has 14 pins that can be driven by the FPGA. 8 of those pins control the seven segments and decimal point. The other 6 pins control which of the 6 digits is on. Note that if each segment on all the digits had its own pin the seven segment display would require 6 x 8 = 48 pins. Reducing the pins to only 14 means that only one number can be shown at a time on the seven segment display digits. Fortunately,​ the human eye can be tricked to think multiple numbers are on at the same time. This is done by displaying each digit enough times per second that the human eye cannot tell the digit is in fact turning on and off. This is referred to as a refresh rate and the book suggests that 50-100 times per second is enough for the human eye. In other words, you must turn on each individual digit of the seven segment display 50-100 times per second.
 +
 +The code in Chapter 6 (copied below) reads B prior to setting any of the bits in B. 
 +<​code>​
 +// Returns 1 if the key at row/col is pressed. Returns 0 otherwise.
 +unsigned char GetSingleButton(unsigned char row, unsigned char col)
 +{
 +  // Set B3..B0 outputs to 1 except the bit at position '​col',​
 +  // ensuring B7..B4 are not modified.
 +     B = (B | 0x0f) & ~(1 << col);  ​
 +  ​
 +   // ​ Now read the input pin at position '​row'​. If the button at 
 +   // ​ row/col is pressed, it will go from pull-up state (1) to 0.
 +   ​return ((A & (1 << row)) == 0 ? 1 : 0);
 +}
 +</​code>​
 +In this example, you want to wiggle the low-order bits (3-0) without modifying the upper bits (7-4). By reading B prior to doing any bit-masking,​ you can accomplish this. On the ZED board, this is unnecessary because the you have set the data direction in main(). In the skeleton file:
 +<​code>​
 +XGpio_SetDataDirection(&​Keypad,​ 1, 0xF0); // Bits set to 1 are input and 0 are output
 +</​code>​
 +sets the direction so that bits 3-0 are output and bits 7-4 are input. As such, you can simply set and read the bits without worrying about their prior values. I modified the book's code slightly to show a version that does not 
 +
 +
 +===== Skeleton File =====
 +
 +This time there are two skeleton files. The first is meant to be copied and pasted into a header file that should be named sevenseg.h. The second file includes "​sevenseg.h"​ and initializes everything you will need in this lab. It also demonstrates writing to the GPIO for the seven segment display.
 +
 +Note that the skeleton file uses two different tick() functions, one for the seven-segment state machine and one for the keypad-scanner state machine. Although it is not required, you can also implement this lab with one state machine; the while-loop would contain a single tick() function in this case.
 +
 +Also note that the while-loop allows you to call the tick() functions at different rates. If you have questions about how this works, see Chapter 8.1 of the text.
 +
 +<​code>​
 +#ifndef SEVENSEG_H_
 +#define SEVENSEG_H_
 +
 +#define ENABLEALL 0x3F00
 +#define ENABLE1 0x0100
 +// Add more enable signals here
 +#define ENABLE6 0x2000
 +
 +#define ALLON 0xFF
 +#define DIGIT0 0x3F
 +// Add more seven segment display digits here
 +#define DIGITF 0x71
 +
 +#endif /* SEVENSEG_H_ */
 +
 +</​code>​
 +
 +<​code>​
 +#include <​stdio.h>​
 +#include "​platform.h"​
 +#include "​xgpio.h"​
 +#include "​sevenseg.h"​ //​ Students need to create this header file
 +#include "​xscugic.h"​
 +#include "​xtmrctr.h"​
 +
 +// Change this value to make timer interrupts more or less frequent
 +#define TIMER_DURATION 200000
 +#define TICKS_PER_SCAN 10
 +
 +XScuGic InterruptController;​      /* Instance of the Interrupt Controller */
 +XScuGic_Config *GicConfig; ​   /* The configuration parameters of the controller */
 +XTmrCtr Timer;
 +XGpio SevenSeg;
 +XGpio Keypad;
 +
 +volatile unsigned int timerFlag = 0;
 +
 +void TimerISR()
 +{
 + timerFlag = 1;
 + // Clear interrupt status bit in control register
 + XTmrCtr_SetControlStatusReg(XPAR_AXI_TIMER_0_BASEADDR,​ 0, XTmrCtr_GetControlStatusReg(XPAR_AXI_TIMER_0_BASEADDR,​ 0));
 +}
 +
 +/​******************************************************************************/​
 +/**
 +*
 +* This function connects the interrupt handler of the interrupt controller to
 +* the processor. ​ This function is seperate to allow it to be customized for
 +* each application. ​ Each processor or RTOS may require unique processing to
 +* connect the interrupt handler.
 +*
 +* @param XScuGicInstancePtr is the instance of the interrupt controller
 +* that needs to be worked on.
 +*
 +* @return None.
 +*
 +* @note None.
 +*
 +****************************************************************************/​
 +int SetUpInterruptSystem(XScuGic *XScuGicInstancePtr)
 +{
 +
 + /*
 + * Connect the interrupt controller interrupt handler to the hardware
 + * interrupt handling logic in the ARM processor.
 + */
 + Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,​
 + (Xil_ExceptionHandler) XScuGic_InterruptHandler,​
 + XScuGicInstancePtr);​
 +
 + /*
 + * Enable interrupts in the ARM
 + */
 + Xil_ExceptionEnable();​
 +
 + return XST_SUCCESS;​
 +}
 +
 +int InitInterrupts()
 +{
 + int Status;
 +
 + /*
 + * Initialize the interrupt controller driver so that it is ready to
 + * use.
 + */
 + GicConfig = XScuGic_LookupConfig(XPAR_PS7_SCUGIC_0_DEVICE_ID);​
 + if (NULL == GicConfig) {
 + return XST_FAILURE;​
 + }
 +
 + Status = XScuGic_CfgInitialize(&​InterruptController,​ GicConfig,
 + GicConfig->​CpuBaseAddress);​
 + if (Status != XST_SUCCESS) {
 + return XST_FAILURE;​
 + }
 +
 + /*
 + * Setup the Interrupt System
 + */
 + Status = SetUpInterruptSystem(&​InterruptController);​
 + if (Status != XST_SUCCESS) {
 + return XST_FAILURE;​
 + }
 +
 + /*
 + * Connect a device driver handler that will be called when an
 + * interrupt for the device occurs, the device driver handler performs
 + * the specific interrupt processing for the device
 + */
 + Status = XScuGic_Connect(&​InterruptController,​ XPAR_FABRIC_TMRCTR_0_VEC_ID,​
 +    ​(Xil_ExceptionHandler)TimerISR,​ &​Timer);​
 +
 + if (Status != XST_SUCCESS) {
 + return XST_FAILURE;​
 + }
 + // Enable the interrupt
 + XScuGic_Enable(&​InterruptController,​ XPAR_FABRIC_TMRCTR_0_VEC_ID);​
 +
 + return XST_SUCCESS;​
 +}
 +
 +int main()
 +{
 +    init_platform();​
 +    int xStatus;
 +    //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + //Step-1 :AXI GPIO Initialization
 + //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + xStatus = XGpio_Initialize(&​Keypad,​XPAR_KEYPAD_8BITS_DEVICE_ID);​
 + if(XST_SUCCESS != xStatus)
 + print("​GPIO INIT FAILED\n\r"​);​
 + xStatus = XGpio_Initialize(&​SevenSeg,​XPAR_SEVSEG_14BITS_DEVICE_ID);​
 + if(XST_SUCCESS != xStatus)
 + print("​GPIO INIT FAILED\n\r"​);​
 + //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + //Step-2 :AXI GPIO Set the Direction
 + //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + XGpio_SetDataDirection(&​Keypad,​ 1, 0xF0); // Bits set to 1 are input and 0 are output
 + XGpio_SetDataDirection(&​SevenSeg,​ 1, 0);
 + //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + //Step-3 :AXI Timer Initialization
 + //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + xStatus = XTmrCtr_Initialize(&​Timer,​XPAR_AXI_TIMER_0_DEVICE_ID);​
 + if(XST_SUCCESS != xStatus)
 + print("​TIMER INIT FAILED \n\r"​);​
 + //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + //Step-5 :Setting timer Reset Value
 + //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + XTmrCtr_SetResetValue(&​Timer,​ XPAR_AXI_TIMER_0_DEVICE_ID,​ TIMER_DURATION);​
 + //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + //Step-6 :Setting timer Option (Interrupt Mode, Auto Reload, Down Count )
 + //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + XTmrCtr_SetOptions(&​Timer,​ XPAR_AXI_TIMER_0_DEVICE_ID,​
 + (XTC_INT_MODE_OPTION | XTC_AUTO_RELOAD_OPTION | XTC_DOWN_COUNT_OPTION));​
 + //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + //Step-7 :Interrupt controller initialization
 + //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + xStatus = InitInterrupts();​
 + if(XST_SUCCESS != xStatus)
 + print("​INTERRUPT INIT FAILED \n\r"​);​
 + ///​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + // Example of writing to the GPIO for the seven segment display
 + // This example enables all the digits and displays a 0
 + //​~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 +    XGpio_DiscreteWrite(&​SevenSeg,​ 1, ENABLEALL | DIGIT0);
 +    // Start the timer
 +    XTmrCtr_Start(&​Timer,​ XPAR_AXI_TIMER_0_DEVICE_ID);​
 +
 +    unsigned int ticksSinceLastScan = 0;
 +    while(1) {
 +    // sevenSegmentUpdateFSM_tick();​ // You will have to implement this function
 +    if(ticksSinceLastScan >= TICKS_PER_SCAN)
 +    {
 +    // keypadScannerFSM_tick();​ // You will have to implement this function
 +    ticksSinceLastScan = 0;
 +    }
 +    while(!timerFlag) {}
 +    timerFlag = 0;
 +    ++ticksSinceLastScan;​
 +    }
 +
 +    return 0;
 +}
 +
 +</​code>​
 +
 +
 +===== Grading =====
 +
 +Show the TA your lab when you believe it is ready. The TA will check that your buttons are responsive and accurate. Nothing incorrect should be displayed on the seven segment display for quick or slow button presses. Turn in your code. Attach a page with the following:
 +
 +  * The period of the refresh rate of your seven segment display
 +  * The period between keypad scans
 +
 +In an effort to improve the course, we also ask for some feedback on your lab experience: ​
 +  * Were there confusing or ambiguous parts of the lab specs? ​
 +  * What major bugs did you have? 
 +  * How many hours did the lab take you? Planning, coding, debugging, total?
 +
 +This feedback doesn'​t need to be lengthy; highlights and approximations are sufficient. We only want you to spend a few minutes on it, but it is a required part of your hand in.
old_lab_5.txt ยท Last modified: 2014/10/15 12:51 by hutch