1. Trang chủ
  2. » Công Nghệ Thông Tin

O''''Reilly Network For Information About''''s Book part 198 potx

6 59 0

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 6
Dung lượng 32,91 KB

Nội dung

flashBase[UNLOCK2_OFFSET] = FLASH_CMD_UNLOCK2; flashBase[COMMAND_OFFSET] = FLASH_CMD_BYTE_PROGRAM; /* * Perform the actual write operation. */ baseAddress[offset] = data[offset]; /* * Wait for the operation to complete or time-out. */ while (((baseAddress[offset] & DQ7) != (data[offset] & DQ7)) && !(baseAddress[offset] & DQ5)); if ((baseAddress[offset] & DQ7) != (data[offset] & DQ7)) { break; } } return (offset); } /* flashWrite() */ /****************************************************************** **** * * Function: flashErase() * * Description: Erase a block of the Flash memory device. * * Notes: This function is specific to the AMD 29F010 Flash * memory. In this device, individual sectors may be * hardware protected. If this algorithm encounters * a protected sector, the erase operation will fail * without notice. * * Returns: O on success. * Otherwise -1 indicates failure. * ****************************************************************** ****/ int flashErase(unsigned char * sectorAddress) { unsigned char * flashBase = FLASH_BASE; /* * Issue the command sequence for sector erase. */ flashBase[UNLOCK1_OFFSET] = FLASH_CMD_UNLOCK1; flashBase[UNLOCK2_OFFSET] = FLASH_CMD_UNLOCK2; flashBase[COMMAND_OFFSET] = FLASH_CMD_ERASE_SETUP; flashBase[UNLOCK1_OFFSET] = FLASH_CMD_UNLOCK1; flashBase[UNLOCK2_OFFSET] = FLASH_CMD_UNLOCK2; *sectorAddress = FLASH_CMD_SECTOR_ERASE; /* * Wait for the operation to complete or time-out. */ while (!(*sectorAddress & DQ7) && !(*sectorAddress & DQ5)); if (!(*sectorAddress & DQ7)) { return (-1); } return (0); } /* flashErase() */ Of course, this is just one possible way to interface to a Flash memory—and not a particularly advanced one at that. In particular, this implementation does not handle any of the chip's possible errors. What if the erase operation never completes? The function flashErase will just keep spinning its wheels, waiting for that to occur. A more robust implementation would use a software time-out as a backup. For example, if the Flash device doesn't respond within twice the maximum expected time (as stated in the databook), the routine could stop polling and indicate the error to the caller (or user) in some way. Another thing that people sometimes do with Flash memory is to implement a small filesystem. Because the Flash memory provides nonvolatile storage that is also rewriteable, it can be thought of as similar to any other secondary storage system, such as a hard drive. In the filesystem case, the functions provided by the driver would be more file-oriented. Standard filesystem functions like open, close, read, and write provide a good starting point for the driver's programming interface. The underlying filesystem structure can be as simple or complex as your system requires. However, a well-understood format like the File Allocation Table (FAT) structure used by DOS is good enough for most embedded projects. [1] 128 kilobytes is one-eighth of the total 1-megabyte address space. [2] The divisor is simply a binary representation of the coefficients of the generator polynomial—each of which is either or 1. To make this even more confusing, the highest-order coefficient of the generator polynomial (always a 1) is left out of the binary representation. For example, the polynomial in the first standard, CCITT, has four nonzero coefficients. But the corresponding binary representation has only three 1's in it (bits 12, 5, and 0). [3] There is one other potential twist called "reflection" that my code does not support. You probably won't need that anyway. [4] There is one small difference worth noting here. The erase and write cycles take longer than the read cycle. So if a read is attempted in the middle of one of those operations, the result will be either delayed or incorrect, depending on the device. Chapter 7. Peripherals  7.1 Control and Status Registers  7.2 The Device Driver Philosophy  7.3 A Simple Timer Driver  7.4 Das Blinkenlights, Revisited Each pizza glides into a slot like a circuit board into a computer, clicks into place as the smart box interfaces with the onboard system of the car. The address of the customer is communicated to the car, which computes and projects the optimal route on a heads-up display. —Neal Stephenson, Snow Crash In addition to the processor and memory, most embedded systems contain a handful of other hardware devices. Some of these devices are specific to the application domain, while others—like timers and serial ports—are useful in a wide variety of systems. The most generically useful of these are often included within the same chip as the processor and are called internal, or on-chip, peripherals. Hardware devices that reside outside the processor chip are, therefore, said to be external peripherals. In this chapter we'll discuss the most common software issues that arise when interfacing to a peripheral of either type. 7.1 Control and Status Registers The basic interface between an embedded processor and a peripheral device is a set of control and status registers. These registers are part of the peripheral hardware, and their locations, size, and individual meanings are features of the peripheral. For example, the registers within a serial controller are very different from those in a timer/counter. In this section, I'll describe how to manipulate the contents of these control and status registers directly from your C/C++ programs. Depending upon the design of the processor and board, peripheral devices are located either in the processor's memory space or within the I/O space. In fact, it is common for embedded systems to include some peripherals of each type. These are called memory-mapped and I/O-mapped peripherals, respectively. Of the two types, memory-mapped peripherals are generally easier to work with and are increasingly popular. Memory-mapped control and status registers can be made to look just like ordinary variables. To do this, you need simply declare a pointer to the register, or block of registers, and set the value of the pointer explicitly. For example, if the P2LTCH register from Chapter 2, were memory-mapped and located at physical address 7205Eh, we could have implemented toggleLed entirely in C, as shown below. A pointer to an unsigned short—a 16-bit register—is declared and explicitly initialized to the address 0x7200:005E. From that point on, the pointer to the register looks just like a pointer to any other integer variable: unsigned short * pP2LTCH = (unsigned short *) 0x7200005E; void toggleLed(void) { *pP2LTCH ^= LED_GREEN; /* Read, xor, and modify. */ } /* toggleLed() */ Note, however, that there is one very important difference between device registers and ordinary variables. The contents of a device register can change without the knowledge or intervention of your program. That's because the register contents can also be modified by the peripheral hardware. By contrast, the contents of a variable will not change unless your program modifies them explicitly. For that reason, we say that the contents of a device register are volatile, or subject to change without notice. The C/C++ keyword volatile should be used when declaring pointers to device registers. This warns the compiler not to make any assumptions about the data stored at that address. For example, if the compiler sees a write to the volatile location followed by another write to that same location, it will not assume that the first write is an unnecessary use of processor time. In other words, the keyword volatile instructs the optimization phase of the compiler to treat that variable as though its behavior cannot be predicted at compile time. Here's an example of the use of volatile to warn the compiler about the P2LTCH register in the previous code listing: volatile unsigned short * pP2LTCH = (unsigned short *) 0x7200005E; It would be wrong to interpret this statement to mean that the pointer itself is volatile. In fact, the value of the variable pP2LTCH will remain 0x7200005E for the duration of the program (unless it is changed somewhere else, of course). Rather, it is the data pointed to that is subject to change without notice. This is a very subtle point, and it is easy to confuse yourself by thinking about it too much. Just remember that the location of a register is fixed, though its contents might not be. And if you use the volatile keyword, the compiler will assume the same. The primary disadvantage of the other type of device registers, I/O-mapped registers, is that there is no standard way to access them from C or C++. Such registers are accessible only with the help of special machine-language instructions. And these processor-specific instructions are not supported by the C or C++ language standards. So it is necessary to use special library routines or inline assembly (as we did in Chapter 2) to read and write the registers of an I/O- mapped device. 7.2 The Device Driver Philosophy When it comes to designing device drivers, you should always focus on one easily stated goal: hide the hardware completely. When you're finished, you want the device driver module to be the only piece of software in the entire system that reads or writes that particular device's control and status registers directly. In addition, if the device generates any interrupts, the interrupt service routine that responds to them should be an integral part of the device driver. In this section, I'll explain why I recommend this philosophy and how it can be achieved. Of course, attempts to hide the hardware completely are difficult. Any programming interface you select will reflect the broad features of the device. That's to be expected. The goal should be to create a programming interface that would not need to be changed if the underlying peripheral were replaced with . flashBase[COMMAND_OFFSET] = FLASH_CMD_BYTE_PROGRAM; /* * Perform the actual write operation. */ baseAddress[offset] = data[offset]; /* * Wait for the operation to complete or time-out. */ while. course, this is just one possible way to interface to a Flash memory—and not a particularly advanced one at that. In particular, this implementation does not handle any of the chip's possible. flashErase will just keep spinning its wheels, waiting for that to occur. A more robust implementation would use a software time-out as a backup. For example, if the Flash device doesn't respond

Ngày đăng: 07/07/2014, 08:20