Académique Documents
Professionnel Documents
Culture Documents
Abstract/ Resumen
IMPLEMENTACION DE APLICACIONES PARA TESTS DE UN
MICROCONTROLADOR
Autor: Alejandre Alba, Guillermo. Director: Vannuffelen, Stephane. Entidad Colaboradora: Schlumberger
La segunda lnea de trabajo consisti en la programacin de un bus SPI (Serial Protocol Interface) entre una FPGA (Field Programmable Gate Array) y el microprocesador. El bus SPI es un estndar de comunicaciones implementado por Motorola y utilizado para la transferencia de informacin entre circuitos integrados en equipos electrnicos. En este caso, el microprocesador hace de maestro, y la FPGA es el esclavo (ver Figure 2). Visto desde el microprocesador, est compuesto de una lnea con el reloj que regula las comunicaciones del bus, una lnea de dato entrante, una de dato saliente, y un pin de activacin (chip select). El cdigo programado en el interior de la FPGA ha sido realizado por una empresa externa, y el objetivo principal de la implementacin de dicho bus fue verificar que la lgica de la FPGA cumpla con las especificaciones dadas a la empresa externa. En lneas generales, la FPGA recoge datos procedentes de un conversor A/D (tambin llamado ADC, Analog to Digital Conversor), los trata, y se los pasa al microprocesador. Adems, ha de ser capaz de enviar seales a un PWM (Pulse-With Modulator). La lgica implementada en el interior de la FPGA esta compuesta por diferentes mdulos por lo que para proceder a su validacin se implementaron diversas funciones. La metodologa utilizada es similar a la de la implementacin de la UART: Siguiendo las especificaciones recibidas por el programador, se implement un simple programa que generara una seal de PWM constante y de valor conocido, que luego se fue mejorando para permitir la interaccin con todos los mdulos de la FPGA. Ello permiti encontrar algunos fallos en el cdigo de la FPGA, que fueron transmitidos a la empresa encargada. El resultado de este mdulo ha sido una aplicacin que permite interaccionar al microprocesador con la FPGA. Configura la FPGA para el tratamiento de datos del ADC y permite controlar el PWM a travs de la FPGA.
DESIGN AND IMPLEMENTATION OF AN APPLICATION USED FOR AUTOMATIC TESTS OF AN ELECTRONIC BOARD
This project consists in different tasks whose meeting point is the microprocessor for which they have been conceived. They aim different peripherals, but share the same microprocessor, a Luminary LM3S2965.
The project has been organized in three tasks clearly defined. The first was the implementation of a UART in order to communicate the microprocessor with a computer. The second was the programming of a SPI between the microprocessor and the FPGA (Field Programmable Gate Array). The third one was the coding of another SPI connection, but this time between the ADC and the microprocessor. UART is the acronym for Universal Asynchronous Receiver/Transmitter. It is a piece of computer hardware that translates between parallel-form data and serial-form data, and vice versa. Two phases can be distinguished in the development of this connection. During the first phase, only the reception and transmission where taken into account. A program was conceived where received data from the computer arrived to the reception FIFO and was stored inside the reception buffer. Then, data was copied to the transmission buffer and written into the transmission FIFO in order to be sent back to the computer. Also, a LED was configured to blink during the whole process, in order to verify that the program was working running. The UART is configured so that the FIFO lists provided by the microprocessor are enabled, as well as some of the preconfigured interruptions inside the microprocessor. The implemented program initializes the system clock and the ports (those related with the LED and the UART), configures the UART, and then gets inside an infinite loop whose only task is to make a LED blink. The reception interruption is enabled every time the amount of data inside the reception FIFO reaches 8 bytes, and reads them. After that, a function whose task is to store the received data in the reception buffer is called. If the received data is not a multiple of 8 bytes, the Timeout interruption is enabled. This interruption alerts the microprocessor whenever there is unread data in the RX FIFO. Once triggered, the microprocessor will keep reading the RX FIFO until it is empty. Afterwards, the data is written in the transmission buffer and the transmission is initialized. In order to start sending data to the computer, the TX FIFO is partially written: every time the amount of data stored in the TX FIFO is below 2 bytes, the transmission interruption is enabled. This interruption partially refills the TX FIFO. Finally, in order to make sure that no data has been left inside the FIFOs, a total reset of the buffers, the FIFOs and the counters is performed. The second part of the UART development was its integration inside a real time operating system. Also, the encapsulation of the communication between computer and microprocessor had to be taken into account: all the data coming from the computer is cased inside a
used to synchronize the ADC with the microprocessor before starting the transmission. The second one is DRDY, which alerts the microprocessor that new data is available in the ADC. This module works as follows: first, the ports that will be used are configured; then, the ADC is synchronized; and finally, we get inside an infinite loop, where we wait for an interruption coming from the DRDY line. As we can see, this module is quite similar to the previous one, except for the use of internal interruptions (triggered by the SPI) and external interruptions (triggered by the ADC).
11
13
15
a. Western Geco
This is mainly a marine and land seismic acquisition and processing company owned and managed by Schlumberger Reservoir Evaluation and Development.
17
Figure 5-SRPC SRPC specializes in: Formation evaluation: Measurements in wells while drilling or via wireline cable. Gives customers real time information about borehole formation. Production-related measurements: When the well is in production, meaning oil or gas is being extracted from the well, production tools gives information such as pressure, temperature or flow rate. These measurements are either used periodically or are permanently installed in wells. Testing of hydrocarbon reservoirs: Downhole transient measurements and equipment to handle produced fluids at surface. Cementing: Design of new chemical formulations for fluids used during well construction. Before producing, the wells casing are made to strengthen the well: this is called cementing.
SRPC is organized in mtiers and disciplines. These two branches are organized as a matrix. The disciplines regroup the segments described previously: Cased-Hole Products, Well Integrity Technology, Formation Evaluation, Interpretation Products, Completions Services, Well Testing Services and Quality Records. The discipline manager is responsible for projects that produce tools and techniques that are required by the oilfield activities. At Clamart, the mtiers are organized under: Chemistry, Electrical, Mechanical, Software & Appl.Maths, Physics and Manufacturing. The Mtier manager is responsible for developing technologies and competences of mtier personnel to world-class level, in line with company product development strategy and timing and in conformity to relevant standards.
19
Figure 7 shows the main elements of a Petroleum Well: Well: Vertical gap man-made perforated on the ground for petroleum extraction purposes. Casing: External metal tube that gives strength to the well. It is normally surrounded by cement for not breaking the different ground layers. Tubing: Inside metal tube concentric with the casing that carries out the petroleum. Its diameter is about 12-14 centimeters. Packer: Pressure valve that makes possible a physical separation between the upper and the lower part of the well. Moreover, the pressure above and below are not the same. It enables the tubing to pass through. Head of well: External part of the casing. It is used for opening and closing the well. Reservoir: Physical deposit where petroleum, gas or oil are stocked.
Project Manager
Physicists
Mechanical Engineers
Firmware Engineer
Telecommunications Engineer
Software Engineer
Electronic Engineers
Mechanical Intern
Firmware Intern
Electronic Technician
Figure 8 MuZIC Team The MuZIC team composition is shown in Figure 8. As we can see, it is highly multidisciplinary and includes electronic engineers, mechanical engineers, physicists, firmware engineers, software engineers and telecommunications engineers. A project manager coordinates the project. Even though the development of the internship has meant working with all of them, most of its work has taken place with the firmware and the telecommunication engineers, Carlos Merino and Guillaume Millot.
21
measures are stored inside a recording device in the testing tool, and read after the testing process, when testing tools a retrieved from the well and taken back to surface. In order to take accurate measures of the reservoir, these tools are set downhole for longer periods of time that they should be. This is a waste of both time and money. Because the packer makes any wired connection impossible, the goal of the MuZIC project is to develop a wireless communication system which would allow communication in real time between testing tools downhole and surface. In parallel with the MuZIC project, other segments are already developing tools that communicate with low frequency electromagnetic signals, but their data rate is not faster enough.
Physical Concepts
Mechanics
Firmware
Electronics
Telecommunication
Surface Software
Simulations
Lab Tests
Field Tests
Figure 9 Parts of the MuZIC project The tasks performed in each of these parts were:
23
3. Microcontrollers choice
The first, and most important reason for choosing the LM3S2965 was that it is able to function under the working conditions established for the MuZIC project: high temperature, high pressure, and long autonomy. Furthermore, it is the microcontroller which consumes the less power given the harsh working conditions. The Luminarys micros have also other properties which make them interesting for industrial applications. First, the ARM architecture used in Luminary Micro enables 32-bit performance for the same price as 8-bit and 16-bit microcontrollers. Moreover, its Cortex-M3 is easier to program (no need of assembly code), uses atomic operations, and is optimized for single-cycle flash usage. Finally, all of the microcontrollers come with Stellaris Peripheral Driver Library, which smoothes interaction with the different peripherals.
25
Being a relatively new company, Luminary microcontrollers use the latest technologies and more advanced and sophisticated solutions. Unfortunately, it is also a bit more unstable than older microcontrollers which have been used in industry for much more applications. We will see trollers some examples regarding this issue in the following sections. The implementation of the different modules has been performed in a LM3S2965 evaluation board, except for the hibernation module, for which different boards were used. In order to meet ibernation the needs of the project, this board was modified during the first months, and the 8MHz quartz was replaced by a 12.8MHz oscillator. The implementation of the different modules has been performed in a LM3S2965 eval evaluation board, except for the hibernation module, for which different boards were used. In order to meet the needs of the project, this board was modified during the first months, and the 8MHz quartz was replaced by a 12.8MHz oscillator.
5. Project Overview
Figure 10 shows a small diagram of how the different electronics are communicated between them.
2. Main definitions
A Universal Asynchronous Receiver/Transmitter (UART) is a piece of computer hardware that translates data between parallel and serial forms. They are commonly used for serial communications over a computer or peripheral device serial port. The UART takes bytes of data and transmits the individual bits in a sequential fashion. At the destination, a second UART re-assembles the bits into complete bytes. Serial transmission of digital information (bits) through a single wire or other medium is much more cost effective than parallel transmission through multiple wires (in terms of reliability, amount of equipment, etc). A UART is used to convert the transmitted information between its sequential and parallel form at each end of the link. Each UART contains a shift register which is the fundamental method of conversion between serial and parallel forms. The UART usually does not directly generate or receive the external signals used between different items of equipment. Typically, separate interface devices are used to convert the logic level signals of the UART to and from the external signaling levels. Nowadays, UARTs are commonly used with RS-232 (a standard for serial binary data signals) for embedded systems communications. For detailed information about the UARTs characteristics of the LM3S2965, please refer to the Datasheet of the LM3S2965.
3. Objective
The microcontroller has to be able to communicate with the PC via surface software whose mission is to act as an interface between the user and the computer. The surface software wraps the users messages in a protocol shared with other tools used in downhole testing, and sends
27
them to the microcontroller. Once there, the microcontroller has to unwraps them, and treat them. All messages coming from the PC need to be answered, and they have to be wrapped before being sent. Table 1 shows the format of the messages using this protocol. Table 1: Format of messages 1 byte SOF A0h 1 byte Length 2 to 255 1 byte Device 1 byte Command N bytes Data 1 byte Checksum
A message sent using this protocol has a maximum size of 255 bytes. SOF means Start Of Frame. Length contains the length of the whole message, including the wrapping. The number of the device depends on the kind of tool which is sending or receiving the message. In our case, the Device value for MuZIC is 5. The users orders are sent in the Command byte. If the message implies data transmission, N bytes are reserved for that purpose (N being a number of bytes between 0 and 250). Finally, a checksum is performed to verify that the message is being transmitted correctly. Hence, this module has to configure the connection between microcontroller and PC, manage the transmission and reception of messages between both, and perform a correct wrapping and unwrapping of the messages.
4. Specifications
Taking into account the previous section, a list of specifications can be sorted out: As we have previously seen, there are more electronic components inside a MuZIC tool. We have seen that the clock speed of the microcontroller depends not only on the extreme working conditions, but also on the ADC, whose clock speed is set to 6.4MHz. The highest speed at which the LM3S2965 can turn with no problems under these conditions is 25.6MHz. But at the time this peripheral was implemented, there were no available quartzes of that speed. So 12.6MHz quartz was used instead. This added a limitation to the UART speed: no reception of messages is guaranteed at that clock speed for baud rates higher than 57600. (See Software UART for Stellaris Microcontrollers Applications Note for more information).
6. Results
The result is a UART module triggered via interruption mainly. The transmission has to be called first in order to be initialized, but once it has been set the transmission uses interruptions to work. As for the reception, an interruption is triggered every time data is received. All the functions are implemented in the file drv_uart.c (Annex A: Peripherals Code). This task manages and controls all low level UART communications on serial port with the PC. It has been designed to: Receive data from the serial port using the reception interruption. Transmit via the serial port using the function StartTransmission (see Annex A: Peripherals Code). Figure 11 shows the UART access module structure:
29
The communication between the PC and the LM3S2965 is done through a RS232 through communication using a RS232 transceiver (TRANSmitter - reCEIVER). This is so because the LM3S2965 evaluation board does not have an implemented transceiver, so an external one had to be added. The maximum possible baud rate was 57600. The implemented UART works with a baud implemented rate of 19200. An inferior baud rate was preferred in order to be sure that the UART was working inside the performance limitations specified in Software UART for Stellaris Microcontrollers Applications Note. It has no parity or even parity, and one stop bit. It sends packets of 8 bits. The . transmission and Reception FIFOs have also been enabled. Before a message is sent, it is written in a 16 byte FIFO list where it waits until the 16-byte microcontroller sends it. The same goes for a message that has just been received. For both FIFOs (there is a transmission FIFO and a reception FIFO), a level has to be defined. This level is used to trigger an interruption each time the amount of data stored inside the FIFO reaches that level. The interruption will then alert the microcontroller, who will read the FIFO (in the case of nterruption , the RX FIFO), or add more data to be sent (TX FIFO). The default level for both FIFOs is 8 bytes. In the case of this UART, the interruptions have been programmed to be triggered every time we have more than 8 bytes in the RX FIFO, and less than 2 bytes in the TX FIFO. Figure 12 shows a small diagram of how the reception works l works.
Figure 12- Reception process All the interruptions from the UART are managed by the function UART0_Handler. This UART0_Handler function is triggered by all the interruptions coming from the UART. It verifies which type of interruption has appeared, then calls the function related with its treatment. There are seven treatment. different types of interruptions: overrun error, break error, parity error, framing error, timeout error, transmission error, and reception error. Interruptions can be enabled or disabled by means of a mask register called Masked Inte Interrupt Status (MIS). This UART has been programmed to detect all of the interruptions except for the break error and the parity error. In order to send a message using the UART, the microcontroller has to call first the function StartTransmission. This function will wait until the UART is free to be used by checking the tion UART flag register. Then, it partially fills in the FIFO, in order to enable the Transmission interruption. The microcontroller has implemented some interruptions that can be used to perform the communication between the PC and the LM3S2965. The FIFO needs to be partially LM3S2965. filled (at least 32 bits must be written into) in order to trigger the interruption related with the transmission of information. This interruption is only triggered whenever the amount of data whenever inside the FIFO goes below a pre determined level (in this case, 1/8 of the total FIFO, 2 bytes). nside pre-determined Once the first interruption has been triggered, the Transmission interruption is called every time the data stored in the FIFO is inferior to that amount, and more data is written inside the TX inferior FIFO. Hence, the transmission works automatically after the first call of StartTransmission. StartTransmission
31
In order to receive a message, a similar system applies: but this time, an interruption is triggered every time the Rx FIFO has more than 8 bytes in store. Thus, the UART has to take into account three different scenarios when receiving a message: 1) Data size is inferior to 8 bytes: In this case, the Reception interruption is never triggered (RX FIFO level has been set to 8 bytes). Nevertheless, the timeout interruption is triggered. This interruption alerts the microcontroller whenever some data has arrived, but hasnt been read yet. It is set 32 bits after the reception of the data. This function reads all the information until there is no more data left inside RX FIFO. Figure 13 shows an example.
Figure 13 UART Reception (less than 8 bytes). The Timeout interruption is triggered; all the RX FIFO is read 2) Data size is equal to 8 bytes (or multiples of 8 bytes): The Reception interruption is triggered, and all the data is read form the FIFO. The process is shown in Figure 14 and Figure 15.
Figure 14 UART Reception (multiple of 8 bytes) (I): The reception interruption is triggered
Figure 15 UART Reception (multiple of 8 bytes) (II): Message read 3) Data size is not a multiple of 8 bytes (and data is superior to 8 bytes): First, the Reception interruption will be triggered, and 8 bytes will be read out of the RX FIFO. Then (32 bits after), as more data is left inside the RX FIFO, the timeout interruption will be triggered, and the rest of the data will be read. Figure 16, Figure 17 and Figure 18 show an example.
33
Figure 16 - UART Reception (more than 8 bytes, not multiple) (I): Reception interruption + Timeout interruption
Figure 17 - UART Reception (more than 8 bytes, not multiple) (II): Reading 8 first bytes
Figure 18 - UART Reception (more than 8 bytes, not multiple) (III): Reading the data left In order to implement the UART module inside the RTOS, a wrapping and an unwrapping function were implemented. The wrapping function takes the data which will be sent to the PC and add the SOF byte, the length of the message, and a checksum. The unwrapping function is used whenever a new message comes from the PC: it reads the type of command sent by the computer, the length of the message and the data (if there is any). In both interruptions, a series of tests are performed in order to verify that the content of the data has not been damaged: this includes checking the SOF byte, the size of the data (which cannot exceed the size of OSCAR buffer), and the total length of the message
7. Remarks
During the implementation of the peripheral, the following issues had to be sorted out: At first, the PC could not understand what the evaluation board was sending. The reason for this was that there was no transducer in the evaluation board. A small setup (including the RS232) was made between computer and LM3S2965 to solve it. Nevertheless, the biggest issue while implementing the UART was the management of the TX and RX FIFOs. Even though the interruption registers were correctly configured, we would not see any interruption trigger when writing in the TX FIFO. This was a problem, because the amount of data sent was necessary in order to check that the whole message was being sent to the PC. The reason was that no flag was set because the amount of data written in the TX FIFO was not enough. The TX packets written in the
35
FIFO had to be at least 16 bytes long to pull up the configured flag. Something similar happened with the RX FIFO. Sometimes, received messages would not have a correct checksum, even though the rest of the message was correct. This depended on the length of the message and the interruption management. We discovered that not only a reception interruption was needed, but also a time-out interruption. The time-out interruption enabled a correct read of the RX FIFO whenever we knew there was data left but the Rx flag would not raise. Finally, there were some issues regarding the code migration from a microcontroller using a system clock of 8MHz (the one included in the evaluation board) to another one using 12.6MHz. This was due to the PLL inside the micro. The PLL only works with system clocks up to 8MHz, and the value of the crystal has to be specified before setting it (See LM3S2965 Datasheet for more information). Although it was not used in our case, we had to make sure that no crystal value was written in the system clock configuration. Otherwise, the PLL got confused and the board got frozen, thus making any reprogramming impossible.
2. Main definitions
A Field-Programmable Gate Array (FPGA) is a semiconductor that can be configured by the customer after manufacturing. They are programmed either using logic circuit diagrams or source code in hardware description language to specify how the chip will work. Hence, they can be used to implement any logical function that a specific integrated circuit could perform, but with the possibility to update the functionality once it is embedded in a circuit. A Serial Peripheral Interface Bus (SPI) is a synchronous serial data link standard named by Motorola that allows simultaneous communication in both directions (from device A to device B and from device B to device A). Devices communicate in master/slave mode where the master device initiates the data frame. Multiple slave devices are allowed with individual slave select (chip select) lines. Sometimes SPI is called a four wire serial bus: the serial clock SCLK (output from master), the serial data in SDI (input for master), the serial data out SDO (output from master), and the chip select nCS (active low, output from master). When working with a single slave device, the nCS can be fixed to active low, but some slaves require the falling edge of the slave select in order to initiate action. To begin a communication, the master first configures the clock using a frequency supported by the slave. Then, it pulls the slave select low. If a waiting period is required (for example, with ADC slaves), then the master must wait for at least that period of time before starting to issue clock cycles. Finally, the full duplex communication takes place: the master sends a bit, the slave reads it: and the slave sends a bit, the master reads it. The four operations take always place, but they might not be always of interest for the user (like in the case of dummy writes from the master in order to receive data from the slave: this is performed in order
37
to give the clock to the slave, so that it can send data using it). Once the transmission is over, the master stops sending the clock, and pulls up nCS. In addition to setting the clock frequency, the master must also configure the clock polarity master (CPOL) and phase (CPHA) respect to the data: At CPOL = 0, the base value of the clock is zero. Inversely, at CPOL = 1, the base value of the clock is one. CPHA=0 means sample on the first clock edge, while CPHA=1 means sample on the second clock edge, regardless of whether that clock edge is rising or falling. Note that , with CPHA=0, the data must be stable for half cycle before the first clock cycle. Figure 19 shows a timing diagram with clock polarity and phase: g
Figure 19 Clock Polarity and Phase For detailed information about the SPIs characteristics of the LM3S2965, please refer to the LM3S2965 Datasheet.
3. Objectives
As we have seen in the Mu MuZIC Presentation of the project, this project tries to bring communication in oilfield operations. Part of the signal processing associated with the transmission (TX) and reception (RX) functions are implemented on the embedded FPGA. FPGA There are two ways in which the TX can function, and one way for the RX. In all modes, the communication between microcontroller and FPGA are done via SPI.
The I/Q complex signal sent by the microcontroller is interpolated by an interpolation factor, then filtered using a Square-Root-Raised Cosine filter. The resulting signal is sampled and shifted to passband, coded as 16-bit data and transformed into a real signal. After this, the signal is interpolated and filtered using another Square-Root-Raised Cosine filter. The resulting signal is finally scaled, and an offset is added in order to obtain positive values. The second implementation of the TX processing is done entirely by the microcontroller. Its output is directly a PWM signal which is transmitted to the FPGA at 100 KHz. It is used to generate the PWM command signals which come out of the FPGA. Lastly, the RX passband processing is performed by the FPGA, and the microcontroller is in charge of the RX baseband processing. Figure 21 shows the data path from ADC to micro.
The ADC signal is first converted to baseband by mixing it with a complex exponential. The result is an I/Q signal which is filtered using a Square-Root-Raised Cosine filter. This procedure is performed five times, one for each of the carrier frequencies. Figure 22 shows a diagram of the process, where Fc is one of the carrier signals.
39
Figure 22 Rx FPGA
In order to implement the different processing modes, the contractor delivered a program which acted as a black box from the point of view of the microcontroller, with the following FPGA inputs and outputs: Inputs: ADC_DIN: Data coming from the ADC. ADC_nDRDY: Pin that tells the FPGA that a new data is ready to be read in the ADC. RST: Active-High Reset. CLK: Main system clock received from a 12.8MHz oscillator (in our case, the microcontrollers oscillator). nCS: SPIs microcontrollers chip select. SCLK: SPIs microcontrollers clock. SDin: SPIs microcontrollers data in. Outputs: ADC_CLK: ADCs main clock. ADC_MODE: ADCs functioning mode ADC_FORM ADC_SCLK: SPIs ADCs clock. ADC_nSYNC: ADCs synchronization pin. PWMout: PWM output. SDout: SPIs microcontrollers data out. DRQ: FPGAs Data Request. RQ50k: FPGAs PWMs data request.
4. Specifications
Considering the previous section, the specifications for the SPI between the FPGA and the microcontroller are the following: The peripheral has to work using interruptions whenever possible. The SPI has to use a clock speed of 2.56MHz. (F/5, where F=12.8MHz, the clock of the FPGA). The messages are 24 bits long and can be divided in two parts: first, the command frame (8 bits), and then the data frame (16 bits). The chip select (nCS) has to be pulled down at the beginning of each transmission and pulled up at the end. The Data line has to be sampled on the falling edge of SCLK (CPOL=0). A library of commands and messages has to be implemented. The microcontroller has to be able to load into the memory of the FPGA the Sinewave Memory (used when sampling), the two FIR filters and the gain and offset of the PWM. Once the FPGA has been duly programmed, the microcontroller has to be able to start a transmission. After the configuration of the FPGA, the microcontroller has to send data in the following situations: o When in the second implementation of the TX processing, a new PWM value has to be sent every rising edge of RQ50K (output FPGA signal). o When in the first implementation of the TX processing, a new I/Q value has to be send every rising edge of DRQ. At the same time, the program has to check that the program inside the FPGA works as specified: Pins have to be assigned to the FPGA, and the program has to be loaded inside, with no errors.
41
All the modules which compose the FPGA program have to be verified, and have to work according to the specifications: o o o o The SPI between the FPGA and the ADC. (adc_if.v) The Transmitter processing block. (coder.v) The Receiver processing block. (decoder.v) The PLL that generates the clock for the ADC and the FPGA (using the one from the micro). (PLL12_8.v) o o o o The Power On Reset module. (por.v) The PWM generator. (pwm.v) The SPI interface with the microcontroller. (spi.v) The internal RAM memory where the tables will be loaded. (sram2.v)
6. Results
The result was a driver that configured the SPI and the FPGA for the Transmission modes. Once the configuration of the processing mode has been done, the module works via interruptions. Every time a rising edge coming from the FPGAs DRQ or RQ50k outputs (depending on the processing mode the micro is working) is received, an interruption is triggered. Since no information was expected to come from the FPGA at this stage, no receiving functions were programmed. All the functions have been implemented in the file called drv_spi_fpga.c (Annex A: Peripherals Code). This task manages and controls all low level SPI communications between the FPGA and the LM3S2965. It has been designed to:
ConfigurationTriggerRQ50k and ConfigurationTriggerDRQ, and handle the interruption , using IntRQ50KHandler and IntDRQHandler. Reset the FPGA using the function SSI_ResetFPGA. Start TX transmission using the function SSI_SendStart. sion Transmit data using the functions SSI_Send8 and SSI_Send16. Stop the SPI transmission using the function SSI_Stop. Figure 23 shows the SPI access module structure.
In order to test the different TX processing modes separately, a test bench was created (spi_fpga_test_bench.c). This enables the user to choose between both modes before programming the microcontroller and testing the selected mode. The desired SPI clock speed was not allowed by the LM3S2965. Instead, we had to choose between 3.20MHz and 2.13MHz (12.8/4 and 12.8/6, respectively). After talking with the contractor, (in order to check which of the two was best supported by the FPGAs program) supported 3.20MHz was chosen because it was faster. It is also compatible with the FPGAs
43
implementation (verified by the contractor). The SPI clock polarity has been set to 0, while the clock phase is 1. The frame format used for the SPI is that defined Motorola. There was no need in enabling the SPIs FIFOs because no data coming from the FPGA was expected and the TXs FIFOs state could be checked in the flag register. First, the program configures the system clock and enables the ports used by the SPI. Then, it configures the interruptions and the SPI. Finally, if we are in the first TX processing mode, PWM values are sent when asked by the FPGA. If the second TX processing mode is being set, the FPGA is configured, and the simulation is started. Even though each TX processing mode uses a different trigger, their configuration is exactly the same except for the number of the input pin used: they both configure the micro to detect rising edges on that pin and enable an interruption. As we previously said, the FPGA is needs to receive 24-bit messages. Unfortunately, the Luminarys SPIs maximum data size of a message is 16bits. So there was no way we could send a 24-bit message, all in one block. The implemented solution pulls down the chip select, then sends two packets of data, and finally pulls up again the chip select. The first packet is an 8-bit packet (the command frame), the second one a 16-bit packet (the data frame). As both packets are sent when the chip select is low, it makes no difference for the FPGA. There is a time delay between both packets, but this could not be helped. The reason for this is that every time the data size of the message is modified, the SPI has to be stopped and reconfigured. Apart from the configuration time, there is a mandatory 3-system-clock-tick-delay after the configuration of a peripheral. A waiting period is also imposed by the FPGA after each transmission, because no reconfiguration is possible while the SPI is busy. Nevertheless, this delay does not keep the SPI from working properly and dealing with the time restrictions imposed by the FPGA program. In order to send a message to the FPGA, the microcontroller has to call the functions SSI_Send8 and SSI_Send16. The first one stops the SPI, reconfigures its data size to be 8 bits, pulls down the chip select, and sends the command frame. Once the data has been handed to the TX FIFO, it also waits until it is not busy anymore. After this, SSI_Send16 is called. Its task is to stop again the SPI, reconfigure it to send 16-bit messages, and send the data frame. When the SPI is not busy anymore, the chip select is pulled up. As we can see in the FPGA configuration functions, no interruptions are needed. Polling has been used instead. This is so because of the nature of the functions: in the final RTOS version, everything is configured before start using interruptions. Configuration has been conceived as a sequential part, and no interruptions are used inside. For this reason, there are some times in which a waiting instruction has been implemented. For example, when loading inside the FPGA
45
Figure 25 Second TX mode Note that in the first TX processing mode, the first I&Q values are sent manually using the function SSI_SendISendQ. After the first values of I&Q have been sent, it enables the DRQ values interruption. The rising edge is detected by the microcontroller, which triggers an interruption. Each , interruption has its own handler ( (IntRQ50KHandler and IntDRQHandler), but they both perform ), the same actions. When any of these functions is called, the interruption is cleared and new data is sent. In the case of IntRQ50KHandler the index from the sine wave table is incremented (or IntRQ50KHandler, reset if the end of the table has been reached). If the values asked by the FPGA are duly sent, a PWM signal can be seen coming out from PWMout. Figure 26 and Figure 27 show some transmissions using the different TX processing modes.
Figure 27 Second TX mode While programming the FPGA with the contractors program, pins were assigned to it. Table 2 shows all the FPGA inputs and outputs, with their associated pin.
47
127 11 2 18 8
SDin SDout
31 35
The FPGA implementation was performed with no errors. Configuring the SPI enabled the test of the following modules: adc_if.c, coder.v, PLL12_8.v, pwm.v, spi.v, sram2.v. All of them were proved to work as expected, except for coder.v, were some minor modifications has to be performed regarding the gain and offset configurations of the PWM.
7. Remarks
This module took most of my internships time due to its complexity and the fact that third partys programs were involved. This needed a deeper understanding of the project, of the specifications handed out to the contractor, and of the solution given by the contractor to those specifications. Trying to connect the different visions of the project was the hardest part of it. Several issues came out during the implementation of this part. As we will see during this part, some of them needed the participation of the contractor, and a meeting was held during the implementation of the SPI in order to clarify some points: The software platform required to program the FPGA with the contractors program was a bit complicated, and some extra time was needed in order to be able to handle the new environment and perform the required operations. Because it was the first time the software was used by the team, a short document describing the steps to follow in order to program the FPGA was written. In order to make sure that the information was being correctly sent via the SPI and written on the FPGAs memory, a reading function was implemented, with no success. Even though the FPGA specifications clearly said that the FPGAs program contained a reading instruction, there was no such instruction. As no reading instruction had been asked in the specification, the contractor did not program one, even though he wrote it in the design documentation of the program. This issue was discussed in a later meeting with the contractor, and it was finally decided that a reading option should be available in
Figure 28 Faulty PWM signal There were various reasons for this. First, the design documentation did not specify the clock phase of the SPI, so it was set to its default value (zero). During the meeting with the contractor, we were told that the needed value was 1, so the configuration of the SPI had to be changed. Also, it was clear that the PWM and the SPI data were clearly not synchronized. At an initial stage, the implemented program did not use an output signal coming from the FPGA to trigger an interruption that would send a new value to the FPGA. A timer of 50 KHz was set instead. Every time a timer interruption was triggered, a new value was sent to the FPGA. This proved to be a misunderstanding of the contractors design documentation from our side. As soon as this was noticed, a new configuration was set, where new PWM values were sent every time the RQ50k signal sent a rising edge. All of these modifications led to a satisfying PWM signal (see figures from the previous section).
49
2. Main Definitions
An Analog-to Digital Converter (ADC) is a device which converts continuous signals to discrete digital numbers. A Digital-to-Analog Converter (DAC) performs the reverse operation. Typically, an ADC is an electronic device that converts an input analog voltage to a digital number proportional to the magnitude of the voltage. Most ADCs are linear, that is, the range of the input value that map to each output value has a linear relationship with the output value. ADCs are used virtually everywhere where an analog signal has to be processed, stored or transported in digital form.
3. Objectives
We have seen in the previous sections that the downhole measures taken by the ADC have to go through the FPGA before arriving to the micro. As long as the FPGA is functioning, the system can work correctly. Unfortunately, there will be times when the FPGA will not be working (for example, in case of an FPGA error). So in some situations it will be necessary to bypass the FPGA and connect the ADC directly to the LM3S2965. The following module has been implemented to make the system work without an FPGA. In this case, the micro will be in charge of configuring the ADC and receiving its data. The ADC used in this part contains the following inputs and outputs:
Inputs SPICLK: The clock of the SPI connection SDin: Data coming from the microcontroller. SYNC: Pin to synchronize the ADC with the microcontroller.
4. Specifications
This module is expected to deliver an SPI between the ADC and the microcontroller with the following characteristics: The ADC has to be calibrated before being used. Thus, a synchronization signal has to be sent for 100ms to the SYNC pin. Once the configuration process has finished, the module has to work exclusively with interruptions. The ADC will inform the microcontroller that new data is available by pulling down a pin (DataReaDY pin). An interruption has to be triggered in order to handle the new information. The ADC does not have a RAM memory, so it does not store data. That means that new received values erase the precedent ones. The programmed module has to make sure that the ADC data is read on time to prevent loss of information. It has to configure the SPI taking into account the needs of the ADC: the ADC data is 24 bits large, but its configuration only allows it to send this data to the microcontroller in messages of 8 bits. The module has therefore to make sure that the 24 bits have correctly arrived. As the master of the SPI connection is the LM3S2965, it is in charge of sending the SPI clock to the ADC. So a dummy write has to be performed every time the ADC has new data to send, in order to supply the ADC with the SPI clock.
51
6. Results
The result is another SPI driver that connected the ADC and the microcontroller directly. Again, this module works via interruptions (once the configuration has been done). This module uses two different types of interruptions: external interruptions (coming from is the ADC), and internal interruptions (coming from the SPI). In order to treat the first ones, the microcontroller is configured to detect a falling edge in the DRDY signal (ADCIC is in charge of ADCIC the LM3S2965s configuration) and the interruptions are handled using InterruptIC, which configuration), InterruptIC simply performs a dummy write on the SPI in order to give the ADC the SPI clock, necessary for the transmission of the ADC data using the SPI. The second type of interruptions are managed by . InterruptSSIADCControl. This handler reads the masked interrupt status of the SPI, and treats the . different types of interruption which may occur: the reception interruption, the time time-out interruption, and the overrun interruption. There is no need in sending data to the ADC (only nd dummy reads are performed), so the overrun interruption has not been treated. Unlike with the UARTs FIFOs, the trigger level cannot be configured. This means that a reception i interruption will only take place when more than half of the RXs FIFO is full (more than 16 bytes). As the nly data is only sent in 8-bit packets, this interruption will never be triggered. In fact, the only case to bit be treated is the time-out interruption. This interruption will appear if received data has not been out This read 32 bits after the data has arrived. When this interruption takes place, a read of the RX is performed, as well as a new dummy write. Figure 29 shows a small diagram of the tasks performed by this module
Figure 29 ADC Module This driver first configures the input pin which will be used by the ADC to tell the microcontroller that new data has arrived, and configures the SPI between this two devices. between Then, it synchronizes the ADC, and enables the interruptions coming from the SPI and the DRDY pin. Finally, it enters an infinite loop where a blinking LED is set in order to check that the program is running. spi_adc_test_bench.c. The test bench used to test this module is stored in o
Figure 30 ADC Interruption Whenever the ADC has new data, DRDY is pulled down. This triggers an interruption which performs a dummy write over the SPI connection ( (SPI TX pin). The clock sent with the ). dummy read is used by the ADC to write on the SPI ( (SPI RX pin). This message triggers the is tri reception interruption, where the first 8 bits of the 24 bit ADCs message are read and stored in 24-bit the microcontrollers memory. At the same time, a dummy write is performed to enable the next s packet to be sent. A counter is also incremented to keep track of the received parts. The reception keep interruption is called 3 times. Once the whole message has been received, the counter is reset. reset Figure 31 and Figure 32 show the SPI communication between the ADC and the he microcontroller. We can see that the whole ADC message is read before a new one arrives. .
53
7. Remarks
In general, this driver did not cause major problems. Anyway, some issues occurred: When programming the reception interruptions, no interruptions were triggered, even though the configuration was correct. This due to the fact that no reception interruption actually took place, and that the one which was actually taking place was the time-out interruption. Once this configured, we were able to read the ADC data. While developing the module, we were surprised to notice that more than the actual number of interruptions were taking place. The reason for this was that the DRDY interruption flag register was not being cleared after the interruption had been treated. Not cleaning the flag caused false interruptions.
2. Main Definitions
Pulse-Width Modulation (PWM) is a very efficient way of providing intermediate amounts of electrical power between fully on fully off. A simple power switch with a typical power source provides full power only, when switched on. PWM is a comparatively recent technique, made practical by modern electronic power switches. Basically, a PWM variable-power scheme switches the power quickly between fully on and fully off in a signal. In any event, the switching rate is much faster than what would affect the load, which is to say the device that uses the power. In practice, applying full power for part of the time does not cause any problems; PWM is very practical. The term duty cycle describes the proportion of on time to off time; a low duty cycle corresponds to low power, because the power is off for most of the time. Duty cycle is expressed in percent, 100% being fully on.
3. Objectives
This module has been conceived to complement the previous module (see The FPGA connection). In order to bypass effectively the FPGA, all of the tasks performed by the later have to be done by the LM3S2965. This includes the generation of the PWM command: once the information from the ADC has been duly treated, it has to be used to generate the PWM command that will diffuse the information over the MuZIC network. The module that will be exposed in this chapter needs to simulate this: it has to take information from the microcontrollers memory and generate a series of PWM pulses.
55
4. Specifications
Taking into consideration the characteristics of the PWM signal generated by the FPGA, this module has to provide a PWM generator with the following characteristics: As the other peripherals, the PWM has to work via interruptions. A couple of left-aligned PWM signals. The second PWM signal has to be generated applying a dead band to the first one. Equation 1 and Figure 33 show both PWM signals, and the relation between them.
Taking into consideration the system clock used by the microcontroller, the dead band has to measure 10 clock ticks (10/12.8MHz seconds).
6. Results
Before explaining the characteristics of the implemented PWM, we will discuss shortly about the functioning of the PWM generator of the LM3S2965, in order to have a better understanding of the results. The LM3S2965 can generate PWM signals using different methods, but they all use the same principle, which is comparing a counter value to a series of user-programmed values. There are two different counting modes. In the first one, the timer counts from the load value to zero, and then goes back to the load value. In the second mode, the timer counts from zero to the load value, then from the load value to zero, and so on. Generally, the first method is used for generating left- or right-aligned PWM signals, while the second one is used for generating center-aligned PWM signals. Since the following module has to reproduce the FPGAs PWM signal, we will focus on the first mode. As we said, there are several comparators (precisely, two) in the PWM generator that monitor the value of the counter. When either of them matches the counter, they output a singleclock-cycle-width High pulse. These qualified pulses are used in the PWM generation process. Since the second PWM signal depends on the first one, only the first of the comparators is used. Figure 34 shows an example of a count-down timer configuration, with the different values used in the PWM generation. The comparator used in our PWM signal is CompA.
57
Figure 34 Count-down Timer configuration The Load value stores the frequency of the PWM output signal (since the PWM uses a timer, the period of the signal is set by the time taken by the timer to count from Load to zero). Note from the previous figure that every time the timer reaches zero, a pulse is generated. This pulse will be used to change the CompA value and generate the final PWM signal. The PWM generator takes the CompA pulse and generates a PWM signal. This signal passes through the dead-band generator, where both output PWM signals are generated. The first output PWM signal is the input signal with the rising edge delayed by a programmable amount. The second output PWM signal is the inversion of the input signal with a programmable delay added between the falling edge of the input signal and the rising edge of this new signal. The result is a pair of active High signals where one is always High, except for a programmable amount of time at transitions where both are Low. As we previously said, the dead-band delay prevents shootthrough current from damaging the power electronics. Figure 35 shows the PWM signal created using the dead-band generator.
The present module contains a driver that configures the PWM peripheral of the microcontroller and generates a couple of PWM signals using a dead-band generator. The timer uses the main system clock to count in the count-down mode, with no synchronization mode (there is no need in synchronizing the two output signals since they already are synchronized) and a frequency of 100 KHz. Both dead bands are set to 380 ns (5 timer counts using a 12.8MHz system clock). Once the configuration of the peripheral has been fully accomplished, the module works via interruptions. Whenever the timer reaches zero, an interruption is triggered. This interruption changes the CompA value in order to get a different PWM output. All the functions have been implemented in the file called drv_pwm.c (Annex A: Peripherals Code). This driver contains the configuration of the PWM peripheral as well as the handler for the interruptions. It has been designed to: Configure the PWM 0 and its interruption triggers using PWM_Configure. Handle the interruption generated by a zero value in the timer to modify the value of the CompA comparator (InterruptPWM0). The function PWM_Configure configures the PWM clock and enables the port used by the PWM 0. Then it sets the PWM outputs, and configures the PWM. It also initializes CompA with a default value. Finally, it configures the PWM interruptions and starts the timer. Every time we reach zero with the timer, the InterruptPWM0 handler is called. This function loads a new CompA value from a table, and resets the table index when the end of the table is reached. Figure 36 and Figure 37 show the resulting output from the implemented module when sending a 5000Hz sinusoid with the 100 KHz PWM of this module.
59
7. Remarks
This was the only peripheral with helpful example codes, so its implementation did not arouse issues worth mentioning. The most delicate aspect of its configuration was understanding how the LM3S2965s PWM worked in order to program the correct configuration.
61
References
1. Manuals
LM3S2965 Datasheet (January 2009 Preliminary edition) Stellaris Peripheral Driver Library Manual Pro ASIC APA 300 Datasheet
2. Websites
Luminary home page: www.luminarymicro.com Luminary forums: http://www.luminarymicro.com/forums Wikipedia: http://en.wikipedia.org Keil Embedded Development tools webpage: http://www.keil.com/
3. Software
Matlab 2008, Keil Microvision 3, Libero 3.8
drv_uart.c
//-------------------------------------------------------// Project ........................... MuZIC // Module ............................ // File .............................. drv_uart.c //-------------------------------------------------------//-------------------------------------------------------// (c) Copyright Schlumberger 2009 - All rights reserved //--------------------------------------------------------
63
/****************************************************************************************/ //INCLUDE /****************************************************************************************/ #include "hw_ints.h" #include "hw_memmap.h" #include "hw_types.h" #include "debug.h" #include "gpio.h" #include "interrupt.h" #include "sysctl.h" #include "lm3s2965.h" #include "uart.h" #include "interrupt.h" #include "ssi.h" #include "oscarProtocol.h" #include "drv_uart.h" /*****************************************************************************************/ /****************************************************************************************/ //GLOBAL CONSTANTS, VARIABLES /****************************************************************************************/ int amount_of_data_left = 0; //Number of bits that have not been sent still by the //UART unsigned long data_to_be_sent = 0; //Data that has not been sent still by the UART unsigned char data[TX_BUF_SIZE]; unsigned char RXBuf[RX_BUF_SIZE]; unsigned char TXBuf[TX_BUF_SIZE]; int TXDataCount = 0; int RXDataCount = 0; int RXLenght = 0; int TXData_length = 0; int TXIndex = 0; unsigned char Cks = 0; int RxFIFO_empty = 0; long unsigned RXFIFO_reading = 0; int reception_error = 0; /****************************************************************************************/ //LOCAL FUNCTIONS /****************************************************************************************/ void InterruptUART0Transmission (void); void InterruptUART0Reception (void); /****************************************************************************************/ //* Function: UAR_Configure //* Task: Configures the registers of the UART 0 /****************************************************************************************/ void UAR_Configure (void) { GPIO_PORTA_AFSEL_R |= 0x03; GPIO_PORTA_DEN_R |= 0x03; SYSCTL_RCGC1_R |= ACTIVATE_UART0; UART0_CTL_R &= DISABLE_UART0; UART0_LCRH_R &= FLUSH_UART0_FIFO; UART0_IBRD_R |= INTEGER_BDR; UART0_FBRD_R |= FRACTION_BDR; UART0_LCRH_R |= UART0_PARAMETERS; //Enable use UART 0 //Puts to zero bit 0 of UARTCTL (disables UART 0) //Puts to zero bit 4 of UARTLCRH (disables FIFO's // UART 0) //Integer portion of the Baud Rate Divisior //Fractional portion of the Baud Rate Divisior //Writing of the desired serial parameters: //Baud Rate: 19196 Data Lenght: 8bits One stop bit //No parity FIFOs enabled //Configuration of the Port A in order to be used //for the UART 0
UART0_ICR_R |= UART0_CLEAR_INTERRUPTIONS;
UART_interruption_status = UART0_MIS_R; if((UART_interruption_status & UART0_OVERRUN_ERROR_INT)) { for (i=0; i< UART0_FIFO_LENGTH; i++) { RXBuf[i] = UART0_DR_R; } (void) UAR_ClearRXBuf (); reception_error = 1; UARTIntClear (UART0_BASE, UART0_OVERRUN_ERROR_INT); } if((UART_interruption_status & UART0_BREAK_ERROR_INT)) { UARTIntClear (UART0_BASE, UART0_BREAK_ERROR_INT); //To do } if((UART_interruption_status & UART0_PARITY_ERROR_INT)) { UARTIntClear (UART0_BASE, UART0_PARITY_ERROR_INT); //To do } if((UART_interruption_status & UART0_FRAMING_ERROR_INT)) { for (i=0; i< UART0_FIFO_LENGTH; i++) { RXBuf[i] = UART0_DR_R; } (void) UAR_ClearRXBuf (); reception_error = 1; UARTIntClear (UART0_BASE, UART0_FRAMING_ERROR_INT); } if((UART_interruption_status & UART0_TIMEOUT_ERROR_INT)) { do { RXBuf[RXDataCount] = UART0_DR_R;
65
RXDataCount++; RxFIFO_empty = UART0_FR_R; RxFIFO_empty = RxFIFO_empty & MASK_RX_EMPTY_FIFO_FLAG; RxFIFO_empty = RxFIFO_empty >> 4; if (RXDataCount==2) { RXLenght = RXBuf[1]+ 3; } } while(RxFIFO_empty != 1); if (RXDataCount == RXLenght) { TXDataCount = RXLenght; RXDataCount = 0; RXLenght = 0; RxFIFO_empty = 0; UARTIntClear (UART0_BASE, UART_INT_RX); (void) UAR_Send (RXBuf); (void) UAR_ClearRXBuf (); } UARTIntClear (UART0_BASE, UART0_TIMEOUT_ERROR_INT); } if((UART_interruption_status & UART0_TRANSMISSION_INT)) { (void)InterruptUART0Transmission (); } if((UART_interruption_status & UART0_RECEPTION_INT)) { (void) InterruptUART0Reception (); } } /****************************************************************************************/ //* Function: UAR_Send //* Task: Prepares all the information and which is going to be sent using the UART // In order to use the interruptions of the micro, the FIFO has to be filled in with // at least 32 bits(with trigger set to 1/8 full FIFO). That is, no interruption will be // triggered if less than 32 bits are sent in the first place /****************************************************************************************/ void UAR_Send (unsigned char *data) { //Local variables unsigned long check_if_uart_busy; int i=0; check_if_uart_busy = UART0_FR_R;//See if the UART 0 is busy (checking bit 3 UART0_FR_R) check_if_uart_busy &= MASK_UART0_BUSY; //If UART 0 is busy, then bit 3 is set while (check_if_uart_busy); //Wait until the UART 0 has finished sending for(i=0;i<TXDataCount;i++) { TXBuf[i] = *data; data++; } for(i=0;i<4;i++) { UART0_DR_R = TXBuf[i]; //Writes in the Tx FIFO 32 bits TXDataCount--; TXIndex++; } } /****************************************************************************************/ //* Function: InterruptUART0Transmission //* Task: Once the Tx FIFO has been initialized, this interruption is triggered every time // the micro detects that there is = < 1/8 of the FIFO full. It cheks if there is data // left to be sent and updates the global variables 'amount_of_data_left' and //Updates information regarding the amount of data and //the data that has to be sent
if(TXDataCount > 0) //if there is data left, the interruption keeps sending data { UART0_DR_R = TXBuf[TXIndex]; TXDataCount--; TXIndex++; } if (TXDataCount <= 0) //if there is no more data, the interruption is disabled { TXIndex = 0; TXDataCount = 0; RXDataCount = 0; RXLenght = 0; UARTIntClear (UART0_BASE, UART_INT_TX); } } /****************************************************************************************/ //* Function: InterruptUART0Reception //* Task: On reception of a message from the computer, this fonction stores the data in a // variable for later use //ISSUES //* This function stores the received information in a variable, but it doesn't trigger any // interruption to tell anyone that it has information /****************************************************************************************/ void InterruptUART0Reception (void) { int end_message = 0; RXBuf[RXDataCount] = UART0_DR_R; RXDataCount++; if (RXDataCount == 1) //Looking for SOF byte... { if(RXBuf[0] == SOF_BYTE) { //TODO Beginning of a new frame, start reception timeout } } if ((RXBuf[0] != SOF_BYTE)) { RXDataCount = 0; //TODO // first received byte is wrong, reset reception } RXBuf[RXDataCount] = UART0_DR_R; RXDataCount++; RXLenght = RXBuf[1]+ 3; //Looking for size of Received message... if (RXLenght > OSCAR_BUF_SIZE) //Total lenght includes SOF byte, Checksum and length //byte itself { RXLenght = OSCAR_BUF_SIZE; } if ( RXDataCount > OSCAR_BUF_SIZE ) { RXDataCount = 0; //TODO //Reset reception } //If the frame has been fully received, stop timeout counter and wake up serial task
67
//TODO RXBuf[RXDataCount] RXDataCount++; RXBuf[RXDataCount] RXDataCount++; RXBuf[RXDataCount] RXDataCount++; RXBuf[RXDataCount] RXDataCount++; RXBuf[RXDataCount] RXDataCount++; RXBuf[RXDataCount] RXDataCount++;
if (RXDataCount == RXLenght) { end_message = 1; //TODO: Instead of putting a '1' in end_message, a flag has to be set TXDataCount = RXLenght; RXDataCount = 0; RXLenght = 0; UARTIntClear (UART0_BASE, UART_INT_RX); (void) UAR_Send (RXBuf); } } /****************************************************************************************/ //* Function: UAR_ClearRXBuf //* Task: Resets the Reception Buffer and its index /****************************************************************************************/ extern void UAR_ClearRXBuf (void) { int i; for(i=0;i<(RX_BUF_SIZE+1);i++) { RXBuf[i] = 0; } RXDataCount = 0; } /****************************************************************************************/ //END /****************************************************************************************/
drv_uart.h
//-------------------------------------------------------// Project ........................... MuZIC // Module ............................ // File .............................. drv_uart.h //-------------------------------------------------------//-------------------------------------------------------// (c) Copyright Schlumberger 2009 - All rights reserved //--------------------------------------------------------
/********************************************************************************************** //GLOBAL DEFINE, MACROS /********************************************************************************************** //UART 0 Control Define #define ACTIVATE_UART0 0x00000001 //Assignment of a clock and functions to UART 0 by the micro
#define UART0_PARAMETERS (UART_PARITY | UART_WORD_LENGHT | UART_FIFO | UART_TWO_STOP_BITS | UART_EVEN_PARITY | UART_PARITY | UART_SEND_BREAK) //UART 0 Interrupt Trigger Conditions #define UART0_RX_FIFO_LEVEL #define UART0_TX_FIFO_LEVEL #define UART0_FIFO_LEVELS 0x00 0x00 //Triggers interruption when RX FIFO is //Triggers interruption when TX FIFO is >= 1/8 full >= 1/8 full
(UART0_RX_FIFO_LEVEL | UART0_TX_FIFO_LEVEL)
//UART 0 Interrupt Configuration Define #define #define #define #define #define UART_OVERRUN_ERROR_INTERRUPT 0x400 //"1" UART_BREAK_ERROR_INTERRUPT 0x200 //"1" UART_PARITY_ERROR_INTERRUPT 0x100 //"1" UART_FRAMING_ERROR_INTERRUPT 0x080 //"1" UART_RECEIVE_TIME_OUT_INTERRUPT 0x040 0x020 0x010 enables interruptions due to Overrun Error enables interruptions due to Break Error enables interruptions due to Parity Error enables interruptions due to Framing Error //"1" enables interruptions due to Overrun // Error //"1" enables interruptions due to Overrun Error //"1" enables interruptions due to Overrun Error
#define UART0_INTERRUPT (UART_OVERRUN_ERROR_INTERRUPT | UART_RECEIVE_TIME_OUT_INTERRUPT | UART_BREAK_ERROR_INTERRUPT | UART_PARITY_ERROR_INTERRUPT | UART_FRAMING_ERROR_INTERRUPT | UART_TRANSMIT_INTERRUPT | UART_RECEIVE_INTERRUPT) #define UART0_OVERRUN_ERROR_INT #define UART0_BREAK_ERROR_INT #define UART0_PARITY_ERROR_INT #define UART0_FRAMING_ERROR_INT #define UART0_TIMEOUT_ERROR_INT #define UART0_TRANSMISSION_INT #define UART0_RECEPTION_INT 0x400 0x200 0x100 0x080 0x040 0x020 0x010 //Overrun Error Interruption bit //Break Error Interruption bit //Parity Error Interruption bit //Framing Error Interruption bit //Time Out Error Interruption bit //Transmission Interruption bit //Reception Interruption bit
#define UART0_CLEAR_INTERRUPTIONS (UART0_OVERRUN_ERROR_INT | UART0_BREAK_ERROR_INT | UART0_PARITY_ERROR_INT | UART0_FRAMING_ERROR_INT | UART0_TIMEOUT_ERROR_INT | UART0_TRANSMISSION_INT | UART0_RECEPTION_INT) //UART 0 Masks #define MASK_UART0_BUSY #define MASK_UART0_FLAG_RX_EMPTY
0x08 0x10
69
/*********************************************************************************************/ #define RX_BUF_SIZE 60 #define TX_BUF_SIZE 60 #define MUZIC_DEVICE 0x06 #define MUZIC_COMMAND 0x10 #define MASK_RX_EMPTY_FIFO_FLAG 0x10 #define MASK_RX_ERRORS 0x0ff /*********************************************************************************************/ //GENERIC API /*********************************************************************************************/ extern void UAR_Configure (void); extern void UAR_Send (unsigned char *data); extern void InterruptUART0Control (void); extern void UAR_ClearRXBuf (void); #endif /*********************************************************************************************/ //END /*********************************************************************************************/
sytem_config.h
//-------------------------------------------------------// Project ........................... MuZIC // Module ............................ // File .............................. system_config.h //-------------------------------------------------------// Description what this module is used for....................... // // Tasks: //-------------------------------------------------------// (c) Copyright Schlumberger 2009 - All rights reserved //--------------------------------------------------------
/********************************************************************************************** *******************/ //GLOBAL DEFINE, MACROS /********************************************************************************************** *******************/ //Pins #define PIN0 0x01 #define PIN1 0x02 #define PIN2 0x04 #define PIN3 0x08 #define PIN4 0x10 #define PIN5 0x20 #define PIN6 0x40 #define PIN7 0x80 //Ports #define #define #define #define #define #define #define #define
/*********************************************************************************************/ //GENERIC API /*********************************************************************************************/ extern void SYS_Enable_Interruptions (void); extern void SYS_Configuration (void);
system_config.c
//-------------------------------------------------------// Project ........................... MuZIC // Module ............................ // File .............................. system_config.c //-------------------------------------------------------// Description ....................... // // Tasks: //-------------------------------------------------------// (c) Copyright Schlumberger 2009 - All rights reserved //-------------------------------------------------------/***************************************************************************************/ //ISSUES /***************************************************************************************/ //* This file contains all the functions that need to be called before using the // microprocessor. //* All the functions related with the configuration of the system are placed here //* Interruptions must be enabled AFTER the configuration of the system and all the // peripherials. Otherwise, the program will not function properly. /***************************************************************************************/ /****************************************************************************************/ //INCLUDE /****************************************************************************************/ #include "common.h" #include "system_config.h" /*****************************************************************************************/
/****************************************************************************************/ //* Function: SYS_Configuration //* Task: Configures the system clock and the peripherials which will be used /****************************************************************************************/ void SYS_Configuration (void) { SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN); SYSCTL_RCGC2_R |= (PORTG | PORTA); SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG); SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0); GPIO_PORTG_DIR_R = PIN2; //Configuration of the pin used by the LED GPIO_PORTG_DEN_R = PIN2; } /****************************************************************************************/ //Activates the use of Port A and G
71
//* Function: SYS_Activate_Interruptions //* Task: Enables the use of interruptions by the system, and enables the interruptions // of the peripherials in use /****************************************************************************************/ void SYS_Enable_Interruptions (void) { IntMasterEnable(); IntEnable(INT_UART0); } /*********************************************************************************************/ //END /*********************************************************************************************/
(void) ConfigurationTriggerRQ50K (); //This will configure the input signal RQ50K coming from the FPGA as a rising edge //interruption (void) SSI_ConfigureSPI(); //Configuration of the SPI between the FPGA and the DSP SysCtlDelay(100); //FPGA Request (To be verified)
IntMasterEnable(); //Enable Interruptions IntEnable(INT_GPIOA); //Enable interruptions coming from the GPIOA (RQ50K interruptions) while (1); #endif /////////////////////////////////////////////////////////////////////////////////////////////// //Test 2: We load into the FPGA the Sinewave, the FIR tables, the PWM Gain and Offset. We start //the transmission and we send a I and a Q every time the FPGA asks for one. /////////////////////////////////////////////////////////////////////////////////////////////// #ifdef TEST_TX_PWM_INDIRECT volatile unsigned long ulLoop; SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN); // Set the clocking to run directly from the crystal. SYSCTL_RCGC2_R |= (PORTG | PORTA); //Activates the use of Port A and G (used by the PWM) SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG); (void) ConfigurationTriggerDRQ (); //This will configure the input signal DRQ coming from the FPGA as a rising edge // interruption (void) SSI_ConfigureSPI(); //Configuration of the SPI between the FPGA and the DSP SysCtlDelay(100); //FPGA Request (To be verified) (void)SSI_ConfigureFPGA(); //We load into the FPGA the Sinewave and FIR 1 and FIR 2 tables. We load the PWM Gain // and Offset (void)SSI_Simulation(); //We send the FC Parameter for DDFS and start the transmission (void) SSI_SendISendQ(); //We send I and Q and enable interruptions coming from DRQ while (1); #endif /////////////////////////////////////////////////////////////////////////////////////////////// //Test 2: We load into the FPGA the Sinewave, the FIR tables, the PWM Gain and Offset. We start //the transmission and we send a I and a Q every time the FPGA asks for one. //ISSUES: The Rx has not been fully tested. Furthermore, the DRQ interruption has not been //configured to work in the Rx mode. On request, it is supposed to perform dummy writes in //order to give a clock to the FPGA where it can write the message to the FPGA. ////////////////////////////////////////////////////////////////////////////////////////////// #ifdef TEST_RX unsigned long ulIdx = 0; volatile unsigned long ulLoop; SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN); // Set the clocking to run directly from the crystal.
73
SYSCTL_RCGC2_R |= (PORTG | PORTA); //Activates the use of Port A and G (used by the PWM) SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG); (void) ConfigurationTriggerDRQ (); //This will configure the input signal DRQ coming from the FPGA as a rising edge //interruption (void) SSI_ConfigureSPI(); //Configuration of the SPI between the FPGA and the DSP SysCtlDelay(100); //FPGA Request (To be verified) (void) SSI_ConfigRx(); //Configure the FPGa to work as Receiver SysCtlDelay(50); (void)SSI_StartDecoder(); IntMasterEnable(); //Enable Interruptions IntEnable(INT_GPIOA); while (1); #endif }
drv_spi_fpga.c
//-------------------------------------------------------// Project ........................... MuZIC // Module ............................ // File .............................. drv_spi_fpga.c //-------------------------------------------------------//-------------------------------------------------------// (c) Copyright Schlumberger 2009 - All rights reserved //-------------------------------------------------------/********************************************************/ //ISSUES //*The conceived FPGA demands transmissions of 8+16. // The following program sends alternates packages of 8 and 16 bits //(thus sending the order in a 8-bit package and the data part in a 16-bit package) /*********************************************************/
//-------------------------------------------------------// Include //-------------------------------------------------------#include "hw_ints.h" #include "hw_memmap.h" #include "hw_types.h" #include "debug.h" #include "gpio.h" #include "interrupt.h" #include "ssi.h" #include "sysctl.h" #include "lm3s2965.h" #include "drv_spi_fpga.h" #include "timer.h" //-------------------------------------------------------// Local Define, macro //-------------------------------------------------------//Sinewave Table static long tableSMicro [SIN_TABLE_LENGTH] = { REMOVED DUE TO CONFIDENTIALITY CLAUSE}; //FIR 1 Table static long tableFIRMicro_1 [NUMBER_FIR1_PARAMS] = { REMOVED DUE TO CONFIDENTIALITY CLAUSE};
/**********************************************************************/ //* Function: SSI_Configure //* Task: Configures the hardware in order to attain the FPGA // needs of on the SPI /*********************************************************************/ void SSI_ConfigureSPI (void) { SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); //Enables the use of the GPIO where the SSI is SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI); //Enables the use of the SSI0 GPIOPinTypeSSI(GPIO_PORTA_BASE, SSI_CLK | SSI_TX | SSI_RX); //Enables SSI clock, TX, RX GPIO_PORTA_DIR_R |= 0x08; //Configuration of the SSI CS GPIO_PORTA_DEN_R |= 0x08; GPIO_PORTA_DATA_R |= 0x08; //SSI CS Initialized at high level SSI0_CR1_R = 0x0000; //Disables SSI operation in order to configure SSI0_CR0_R = (SCR | SPH | SPO | SPH | FRF | DSS_16); //SSI0 Configuration //Bit rate = FSSIClk / (CPSDVSR*(1+SCR)) = 3.20MHz //Bits 15-8: SCR = 1 //Bit 7: SPH = 1 (Serial Clock Phase)
75
//Bit 6: SPO = 0 (Serial Clock Polarity) //Bits 5,4: FRF = 0 (Frame Format = SPI) //Bits 3-0: DSS = 16 (Data Size Select = 16 bits by default) SSI0_CPSR_R = 0x0002; //Clock Prescale has to be an even number between 2 and 256 SSI0_CR1_R = 0x0002; //Enables SSI operation again } /*********************************************************************************************/ //* Function: SSI_Stop //* Task: Disables the SPI /*********************************************************************************************/ void SSI_Stop (void) { SSI0_CR1_R = 0x0000; //Stops the SSI } /*********************************************************************************************/ //* Function: SSI_Send8 //* Task: Sends 8 bits data via SPI. First, it configures the SPI channel to a data size of // bits. // It waits for three periods, then pulls dowm the Chip Select,and finally sends the data. // Afterwards, we wait until we make sure that all the data has been correctly sent. //* This function is meant to be used to send the type of order to the FPGA //* WARNING!: In this case, the Chip Select won't be pulled up until the data part of the // message (sent via SSI_Send16)has not been sent. /*********************************************************************************************/ void SSI_Send8(long unsigned int data_to_be_sent) { SSI0_CR1_R = 0x0000; //Disable SSI operations in order to configure SSI0_CPSR_R = 0x0002; //Set the Pre Scaler to 2. The Pre Scaler has to be an EVEN number between 2 and 256 SSI0_CR0_R = (SCR | SPH | SPO | SPH | FRF | DSS_8); //SPI Configuration SSI0_CR1_R = 0x0002; //Enable SSI operations SysCtlDelay (3); //The micro has to wait for 3 periods in order to use the peripheral GPIO_PORTA_DATA_R &= ~(0x08); //Pulling down Chip Select to start transmission (void) SSIDataPut(SSI_BASE, data_to_be_sent); while ((SSI0_SR_R & IS_SPI_BUSY)!=0); //We wait until the SPI is not busy anymore //GPIO_PORTA_DATA_R |= 0x08; //Release the SSI Chip Select } /*********************************************************************************************/ //* Function: SSI_Send16 //* Task: Sends 16 bits data via SPI. First, it configures the SPI channel to a data size of 16 // bits. // It waits for three periods, sends the data, and finally pulls up the Chip Select. We also // wait until we make sure that all the data has been correctly sent. //* This function is meant to be used to send data to the FPGA //* WARNING!: We don't need to pull down the Chip Select because it was already pulled down by // SSI_Send8, so we just have put it back to its original level. /*********************************************************************************************/ void SSI_Send16(long unsigned int data_to_be_sent) { //Sending data
//GPIO_PORTA_DATA_R &= ~(0x08); //Pulling down Chip Select to start transmission (void) SSIDataPut(SSI_BASE, data_to_be_sent); while ((SSI0_SR_R & IS_SPI_BUSY)!=0); //We wait until the SPI is not busy anymore GPIO_PORTA_DATA_R |= 0x08; //Release the SSI Chip Select } /*********************************************************************************************/ //* Function: ConfigurationTriggerRQ50K //* Task: Configures Pin 6, Port A to be the input of the Data Request Signal at 50KHz sent by // the FPGA This sets an interruption which is triggered every time we have a rising edge in // the RQ50K signal sent by the FPGA. /*********************************************************************************************/ void ConfigurationTriggerRQ50K (void) { GPIO_PORTA_ICR_R //Clear all edge GPIO_PORTA_DEN_R GPIO_PORTA_IS_R = (PIN7 | PIN6 | PIN5 | PIN4 | PIN3 | PIN2 | PIN1 | PIN0); interruptions = PIN6; //Set pin 6 as input = PIN6_SET_TO_EDGE_SENSITIVE; //Set edge-sensitive interruptions GPIO_PORTA_IBE_R = CONTROLLED_BY_INTERRUPT_EVENT; //Interrupt generation controlled by the Interrupt Event (GPIOIEV) GPIO_PORTA_IEV_R = RISING_EDGE_INTERRUPTION; //Configure pin 6 to trigger an interruption when rising edges GPIO_PORTA_IM_R = PIN6; //Interruptions coming from pin 6 are allowed GPIO_PORTA_ICR_R = (PIN7 | PIN6 | PIN5 | PIN4 | PIN3 | PIN2 | PIN1 | PIN0); //Clear all interruptions } /*********************************************************************************************/ //* Function: IntRQ50KHandler //* Task: Handler for the interruptions coming from GPIO A Pin 6 used by the FPGA for // synchronisation. This interruption is triggered every time the micro sees a rising edge in // pin 6, port A. /********************************************************************************************** ******************/ void IntRQ50KHandler(void) { //Local variables unsigned long data; //Code GPIO_PORTA_ICR_R = PIN6; //Clear interruptions from pin 6 data = (CMD_WRITE | DIRECT_PWM); (void) SSI_Send8(data); //data = ((50*i_index)*256) + ((50*i_index)+25); //i_index++; //Sending the command (Direct PWM) data = ((Sinewave5000Hz[index_sinewave5000Hz]*256)+Sinewave5000Hz[index_sinewave5000Hz+1]); index_sinewave5000Hz = index_sinewave5000Hz+2;
77
(void) SSI_Send16(data); //Sending the 5000Hz Sinewave (for debugging purposes) if (index_sinewave5000Hz == SINEWAVE_5000HZ_LENGHT) //If we arrive to the end of the table, we reset the index table { index_sinewave5000Hz = 0; } } /*********************************************************************************************/ //* Function: SSI_ConfigureFPGA //* Task: Writes on the FPGA the Sinewave Memory Table, the FIR 1 table, the FIR 2 table, the // PWM gain and offset /*********************************************************************************************/ void SSI_ConfigureFPGA (void) { //Code (void) SSI_ResetFPGA (); //Reset FPGA before starting configuration (void)SinewaveMemory(); //Load the Sinewave Memory into the FPGA (void)FIRCoeffTable1(); //Load the FIR Coefficient Table 1 (void)FIRCoeffTable2(); //Load the FIR Coefficient Table 2 (void)PWMGainOffset (); //Load PWM Gain and Offset } /*********************************************************************************************/ //* Function: SSI_ResetFPGA //* Task: Resets the FPGA (done prior to the configuration of the FPGA in order to make it // correctly) /*********************************************************************************************/ void SSI_ResetFPGA (void) { //Local variables unsigned long data_to_be_sent; //Code data_to_be_sent = CMD_WRITE | COMMAND; (void)SSI_Send8(data_to_be_sent); data_to_be_sent = RESET_FPGA; (void)SSI_Send16(data_to_be_sent); SysCtlDelay(30); //FPGA Request (To be verified) } /******************************************************************************************/ //* Function: SinewaveMemory //* Task: Writes on the FPGA the Sinewave Memory Table //* WARNING!: It takes more time to send the data to the FPGA than to write them down in // the TX FIFO. For that reason, we have to wait if we see that the TX FIFO is full, in order // to prevent loss of data. // Every time we send a 24 bit message to the TX FIFO, we check if the TX FIFO is full. If the // TX FIFO is full, we wait until some data has been transmitted to the FPGA. /*********************************************************************************************/ static void SinewaveMemory (void) { //Local variables unsigned long data_to_be_sent; int i; //Code data_to_be_sent = CMD_SET_ADDR | MIXER_SINE_TABLE; //Gives the address of the Sinewave Table (void)SSI_Send8(data_to_be_sent); data_to_be_sent = SINEWAVE_ADDRESS; (void)SSI_Send16(data_to_be_sent); for(i=0;i<SIN_TABLE_LENGTH;i++)
index_fir1_table = 0; //Before loading the table into the FPGA, we reset the index for(i=0;i<NUMBER_FIR1_PARAMS;i++) //Load the table into the FPGA Memory { data_to_be_sent = CMD_WRITE | FIR_COEFF; (void)SSI_Send8(data_to_be_sent); data_to_be_sent = (tableFIRMicro_1 [index_fir1_table]); index_fir1_table++; (void)SSI_Send16(data_to_be_sent); while(!((SSI0_SR_R & IS_SSI_TX_FIFO_NOT_FULL)>>1)); //If the SPI TX FIFO is full, we wait until there is enough space to keep on //loading the table } SysCtlDelay(10); } /*********************************************************************************************/ //* Function: FIRCoeffTable2 //* Task: Writes on the FPGA the FIRCoeffTable1 //* WARNING!: It takes more time to send the data to the FPGA than to write them down in the TX // FIFO. For that reason, we have to wait if we see that the TX FIFO is full, in order to // prevent loss of data. // Every time we send a 24 bit message to the TX FIFO, we check if the TX FIFO is full. If the // TX FIFO is full, we wait until some data has been transmitted to the FPGA. /*********************************************************************************************/ //FPGA Request (To be verified)
79
static void FIRCoeffTable2 (void) { //Local variables unsigned long data_to_be_sent; int i; //Code data_to_be_sent = CMD_SET_ADDR | FIR_COEFF; (void)SSI_Send8(data_to_be_sent); data_to_be_sent = FIR2_MEM_ADDRESS; //...to the FIR 2 Table address (void)SSI_Send16(data_to_be_sent);
index_fir2_table = 0; //Before loading the table into the FPGA, we reset the index for(i=0;i<NUMBER_FIR2_PARAMS;i++) //Load the table into the FPGA Memory { data_to_be_sent = CMD_WRITE | FIR_COEFF; (void)SSI_Send8(data_to_be_sent); data_to_be_sent = (tableFIRMicro_2 [index_fir2_table]); index_fir2_table++; (void)SSI_Send16(data_to_be_sent); while(!((SSI0_SR_R & IS_SSI_TX_FIFO_NOT_FULL)>>1)); //If the SPI TX FIFO is full, we wait until there is enough space to keep on // loading the table } SysCtlDelay(10); } /**********************************************************************/ //* Function: PWMGainOffset //* Task: Writes on the FPGA the FIRCoeffTable1 /*********************************************************************/ void PWMGainOffset (void) { //Local variables unsigned long data_to_be_sent; //Code data_to_be_sent = CMD_SET_ADDR | FIR_COEFF; //Setting the pointer... (void)SSI_Send8(data_to_be_sent); data_to_be_sent = PWM_GAIN_OFFSET_ADDRESS; //...to the Gain & Offset memory address of the FPGA (void)SSI_Send16(data_to_be_sent); data_to_be_sent = CMD_WRITE | FIR_COEFF; (void)SSI_Send8(data_to_be_sent); data_to_be_sent = PWM_GAIN; (void)SSI_Send16(data_to_be_sent); data_to_be_sent = CMD_WRITE | FIR_COEFF; (void)SSI_Send8(data_to_be_sent); data_to_be_sent = PWM_OFFSET; (void)SSI_Send16(data_to_be_sent); SysCtlDelay(10); //FPGA Request (To be verified) //Write PWM Gain //FPGA Request (To be verified)
} /********************************************************************************************/ //* Function: SSI_Simulation(void) //* Task: Launches the simulation of the Test Bench /*********************************************************************************************/ void SSI_Simulation(void) { (void)FCParameter(); (void)SSI_SendStart(); } //Send the FC Parameter for DDFS //Start Transmission
data_to_be_sent = CMD_WRITE | FC_MOD_FREQ_Q; //We send the Frequency Carrier (Q Part) (void)SSI_Send8(data_to_be_sent); data_to_be_sent = DDFS_MSWORD; (void)SSI_Send16(data_to_be_sent); data_to_be_sent = CMD_WRITE | FC_MOD_FREQ_I; //We send the Frequency Carrier (I Part) (void)SSI_Send8(data_to_be_sent); data_to_be_sent = DDFS_LSWORD; (void)SSI_Send16(data_to_be_sent); SysCtlDelay(10); //FPGA Request (To be verified)
} /*********************************************************************************************/ //* Function: SSI_SendStart(void) //* Task: Sends a start (starts transmission) /*********************************************************************************************/ void SSI_SendStart(void) { //Local variables unsigned long data_to_be_sent; //Code data_to_be_sent = CMD_WRITE | COMMAND; (void)SSI_Send8(data_to_be_sent); data_to_be_sent = START_TX; (void)SSI_Send16(data_to_be_sent); SysCtlDelay(30); //FPGA Request (To be verified)
} /*********************************************************************************************/ //* Function: SSI_SendISendQ(void) //* Task: Sends I then Q. Once the first values of I and Q have been sent, we enable the DRQ // trigger. Every time the FPGA asks for a new couple (I and Q), an interruption will send // them. /*********************************************************************************************/ void SSI_SendISendQ(void) { //Local variables unsigned long data_to_be_sent; //Code data_to_be_sent = CMD_WRITE | I_DATA; (void)SSI_Send8(data_to_be_sent); data_to_be_sent = I_MESSAGE; (void)SSI_Send16(data_to_be_sent); data_to_be_sent = CMD_WRITE | Q_DATA; (void)SSI_Send8(data_to_be_sent); data_to_be_sent = Q_MESSAGE; (void)SSI_Send16(data_to_be_sent);
//Sends I
//Sends Q
IntMasterEnable(); //Enable all interruptions IntEnable(INT_GPIOG); //Enagble interruptions coming from Port G (DRQ interruptions)
81
} /*********************************************************************************************/ //* Function: ConfigurationTriggerDRQ //* Task: Configures Pin 6, Port A to be the input of the Data Request Signal at 50KHz sent by // the FPGA This sets an interruption which is triggered every time we have a rising edge in // the RQ50K signal sent by the FPGA. /*********************************************************************************************/ void ConfigurationTriggerDRQ (void) { //Code GPIO_PORTG_ICR_R= (PIN7 | PIN6 | PIN5 | PIN4 | PIN3 | PIN2 | PIN1 | PIN0); //Clear all edge interruptions GPIO_PORTG_DEN_R|= PIN6; //Set pin 6 as input without touching the other pins GPIO_PORTG_IS_R = PIN6_SET_TO_EDGE_SENSITIVE; //Set edge-sensitive interruptions GPIO_PORTG_IBE_R = CONTROLLED_BY_INTERRUPT_EVENT; //Interrupt generation controlled by the Interrupt Event (GPIOIEV) GPIO_PORTG_IEV_R = RISING_EDGE_INTERRUPTION; //Configure pin 6 to trigger an interruption when rising edges GPIO_PORTG_IM_R = PIN6; //Interruptions coming from pin 6 are allowed GPIO_PORTG_ICR_R = (PIN7 | PIN6 | PIN5 | PIN4 | PIN3 | PIN2 | PIN1 | PIN0); //Clear all interruptions } /*********************************************************************************************/ //* Function: InterruptDRQ //* Task: Handler for the interruptions coming from GPIO G Pin 6 used by the FPGA for data // request. This interruption is triggered every time the microprocessor sees a rising edge in // the data request signal coming from the FPGA. Then, it sends I and Q. /*********************************************************************************************/ void IntDRQHandler(void) { //Local Variables unsigned long data_to_be_sent; //Code GPIO_PORTG_ICR_R = PIN6;
data_to_be_sent = CMD_WRITE | I_DATA; (void)SSI_Send8(data_to_be_sent); data_to_be_sent = I_MESSAGE; (void)SSI_Send16(data_to_be_sent); data_to_be_sent = CMD_WRITE | Q_DATA; (void)SSI_Send8(data_to_be_sent); data_to_be_sent = Q_MESSAGE; (void)SSI_Send16(data_to_be_sent);
//Send Q
} /*********************************************************************************************/ //* Function: SSI_ConfigRx(void) //* Task: Sends the 5 frequencies that will be used in the FPGA for parallel decoding. This // includes the frequency, its phase, its sinus, and its cosinus. /*********************************************************************************************/ void SSI_ConfigRx(void) { //Local variables unsigned long data_to_be_sent; int i = 0; //Code data_to_be_sent = CMD_SET_ADDR | FIR_COEFF; //Setting the pointer... (void)SSI_Send8(data_to_be_sent); data_to_be_sent = FIR2_MEM_ADDRESS; //...to the FIR 2 Table address (FIR Address 192) (void)SSI_Send16(data_to_be_sent);
83
//Code ssi_interruption_status = SSI0_MIS_R; if((ssi_interruption_status & IS_SSI_RECEIVE_FIFO_INT)) { data_read = SSI0_DR_R; //We read the data coming from the ADC and store it in the memory if (i_index == SIN_TABLE_LENGTH) { i_index = 0; //We reset the counter of the ADC } else { i_index++; data_to_be_sent = CMD_READ | MIXER_SINE_TABLE; (void)SSI_Send8(data_to_be_sent); data_to_be_sent = DUMMY_WRITE; (void)SSI_Send16(data_to_be_sent); } } if((ssi_interruption_status & IS_SSI_TIME_OUT_INT)) { data_read = SSI0_DR_R; if (i_index == SIN_TABLE_LENGTH) { i_index = 0; } else { i_index++; data_to_be_sent = CMD_READ | MIXER_SINE_TABLE; (void)SSI_Send8(data_to_be_sent); data_to_be_sent = DUMMY_WRITE; (void)SSI_Send16(data_to_be_sent); } } if((ssi_interruption_status & IS_SSI_OVERRUN_INT)) { //TODO } }
// ================================================== // End
drv_spi_fpga.h
//-------------------------------------------------------// Project ........................... MuZIC // Module ............................ // File .............................. drv_spi_fpga.h //-------------------------------------------------------//-------------------------------------------------------// (c) Copyright Schlumberger 2009 - All rights reserved //-------------------------------------------------------#ifndef _DRV_SPI_FPGA_H_ #define _DRV_SPI_FPGA_H_ //-------------------------------------------------------// Glocal Define, macro //-------------------------------------------------------//Microprocessor Pins #define PIN0 0x01 #define PIN1 0x02 #define PIN2 0x04
//Microprocessor Ports #define PORTA #define PORTB #define PORTC #define PORTD #define PORTE #define PORTF #define PORTG #define PORTH
//Rising edge interruption trigger configuration #define PIN6_SET_TO_EDGE_SENSITIVE 0x00 #define CONTROLLED_BY_INTERRUPT_EVENT 0x00 #define RISING_EDGE_INTERRUPTION 0x40 //SSI Configuration //Pin Configuration #define SSI_CLK #define SSI_CS #define SSI_RX #define SSI_TX //SPI Configuration #define SCR #define SPH #define SPO #define FRF #define DSS_8 #define DSS_16 //Data size configuration #define DATA_SIZE_8 #define DATA_SIZE_16 //Micro-FPGA Communication //Operational Code #define CMD_NOP #define CMD_SET_ADDR #define CMD_READ #define CMD_WRITE pointer //Channel (Generic requests) #define COMMAND #define FIR_COEFF #define MIXER_SINE_TABLE #define DIRECT_PWM #define DECODER_COEFF //Channel: I Data (nI_Q = 0) #define I_DATA #define FC_MOD_FREQ_I //Channel: Q Data (nI_Q = 1) #define Q_DATA #define FC_MOD_FREQ_Q
GPIO_PIN_2 GPIO_PIN_3 GPIO_PIN_4 GPIO_PIN_5 (1<<8) (1<<7) (0<<6) (0<<4) (0x7) (0xf) 8 16 //SCR = 1 //SPH = 1 //SPO = 0 //Freescale SPI Format //Data Size Select = 8 //Data Size Select = 16
//NOP //Set adress //Read & increment pointer //Write to adress & increment
//Command/ Status regsiters //FIR Coefficients (128x16) //Mixer Sine table (256x16) //Direct PWM //Decoder 145 FIR Coefficients Table (256x16) 0x02 0x0A //I-Data //Fc Transmit Modulation Frequency (15:0) //Q-Data //Fc Transmit Modulation Frequency (31:16)
0x03 0x0B
//Memory Adresses of the FPGA #define SINEWAVE_ADDRESS #define FIR1_MEM_ADDRESS #define FIR2_MEM_ADDRESS #define PWM_GAIN_OFFSET_ADDRESS
//Masks #define LSB_MASK 0x00FF //Least Significant Bit's Mask #define IS_SPI_BUSY 0x0010 //If the SPI is busy, or the transmit FIFO is not empty, bit 4 SSISR register is set to 1 #define IS_SSI_TX_FIFO_NOT_FULL 0x02
85
//If the Tx FIFO is not full, bit 1 SSISR register is set to 1 #define IS_SSI_RECEIVE_FIFO_INT 0x4 //To check if the received interruption coming from the SSI is due to a received message #define IS_SSI_TIME_OUT_INT 0x2 //To check if the received interruption coming from the SSI is due to a time-out #define IS_SSI_OVERRUN_INT 0x1 //To check if the received interruption coming from the SSI is due to an overrun //Sinewave Coefficient Values #define SIN_TABLE_LENGTH //FIR Coefficient Values #define NUMBER_FIR1_PARAMS #define NUMBER_FIR2_PARAMS //TX Messages #define START_TRANSMITTER #define START_RECEIVER #define DUMMY_WRITE #define PWM_GAIN #define PWM_OFFSET #define DDFS_MSWORD 0x4000 #define DDFS_LSWORD 0x0000 #define START_TX 0x0001 #define I_MESSAGE 30000 #define Q_MESSAGE 30000 #define RESET_FPGA 0x0000
256 145 49
//Lenght of the Sinewave Mixer Table //Lenght of the FIR 1 Table //Lenght of the FIR 2 Table
0x0001 //Starts tranmission 0x0002 //Starts reception 0xaaaa //Message sent when doing a dummy write 512 //Gain of the PWM 128 //Offset of the PWM //Frequency of the carrier (Most Significant Word) //Frequency of the carrier (Least Significant Word) //Starts Tx transmission (sets bit 0) //Message when writing to I //Message when writing to Q //Resets the FPGA
//Rx Define #define FIVE_CARRIER_FREQUENCIES 5 //Carrier frequencies used when coding and decoding //RX Messages #define PHASE_HIGH #define PHASE_LOW #define COSINUS #define SINUS #define START_DECODER
//Other #define SINEWAVE_5000HZ_LENGHT 20 //Lenght of the 5000Hz Sinewave table (For test purposes) //-----------------------------------------------------------------// Generic API //-----------------------------------------------------------------//Configuration of the SPI and the FPGA extern void SSI_ConfigureSPI (void); extern void SSI_ConfigureFPGA (void); extern void ConfigurationTriggerRQ50K (void); extern void ConfigurationTriggerDRQ (void); //Sending via SPI extern void SSI_Send16(long unsigned int data_to_be_sent); extern void SSI_Send8(long unsigned int data_to_be_sent); //Interruptions extern void IntRQ50KHandler (void); extern void IntDRQHandler(void); extern void IntSSIHandler(void); //TX Bench (PWM indirect) extern void SSI_Simulation(void); extern void SSI_SendISendQ(void); extern void SSI_ResetFPGA (void); extern void SSI_SendStart(void); //RX Bench extern void SSI_ConfigRx(void); extern void SSI_StartDecoder(void); #endif
3. ADC
#include #include #include #include #include #include #include #include #include #include #include #include #define #define #define #define "hw_ints.h" "hw_memmap.h" "hw_types.h" "debug.h" "gpio.h" "interrupt.h" "ssi.h" "sysctl.h" "lm3s2965.h" "timer.h" "system_config.h" "drv_spi0_adc.h" GPIO_PIN_3 GPIO_PIN_2 GPIO_PIN_5 GPIO_PIN_4 1
#define WAIT_1_CYCLE
int main(void) { //Local variables unsigned long ulIdx = 0; volatile unsigned long ulLoop; //Code (void) SYS_Configuration (); (void) ADCIC (); (void) ADC_SSI_Configure(); (void) ADC_Calibration(); //SysCtlDelay(100); (void) SYS_Enable_Interruptions (); while (1) { GPIO_PORTG_DATA_R |= 0x04; for(ulLoop = 0; ulLoop < 2000000; ulLoop++) { } GPIO_PORTG_DATA_R &= ~(0x04); for(ulLoop = 0; ulLoop < 2000000; ulLoop++) { } ulIdx++; } } /*********************************************************************************************/ //END /*********************************************************************************************/ //-------------------------------------------------------// Project ........................... MuZIC // Module ............................ // File .............................. drv_spi0_adc.c //-------------------------------------------------------//-------------------------------------------------------// (c) Copyright Schlumberger 2009 - All rights reserved //--------------------------------------------------------
87
//-------------------------------------------------------// Include //-------------------------------------------------------#include "hw_ints.h" #include "hw_memmap.h" #include "hw_types.h" #include "debug.h" #include "gpio.h" #include "interrupt.h" #include "ssi.h" #include "sysctl.h" #include "lm3s2965.h" #include "drv_spi0_adc.h" #include "timer.h" #include "system_config.h" #include "comp.h" //-------------------------------------------------------// Global constants, variables //-------------------------------------------------------int adc_counter = 0; acquisition of data from the ADC unsigned long int ADC_received_data [4]; the ADC unsigned short int dummy_write = DUMMY_WRITE; clock //-------------------------------------------------------// Local functions //-------------------------------------------------------void InterruptIC(void);
//Counter for the //To store the received data from //Used to give the ADC the SPI
/********************************************************************************************** ******************/ //* Function: ADC_SSI_Configure //* Task: Configures the hardware in order to attain the ADC needs on the SPI. // Inputs: RX, // Outputs: SYNC, TX, SPI Clock /********************************************************************************************** ******************/ extern void ADC_SSI_Configure (void) { //Code SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); the use of the GPIO where the SSI is SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI); //Enables the use of the SSI0 GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_7); //We set the SYNC pin as an output GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_7, GPIO_PIN_7); //We initialize SYNC at a high level GPIOPinTypeSSI(GPIO_PORTA_BASE, SSI_CLK | SSI_TX | SSI_RX); //Enables SSI clock, TX, RX SSI0_CR1_R = DISABLE_SSI; //Disables SSI operation in order to configure //SSI0 Configuration //Bit rate = FSSIClk / (CPSDVSR*(1+SCR)) = 2.13MHz //Bits 15-8: SCR = 2 //Bit 7: SPH = 0 (Serial Clock Phase) //Bit 6: SPO = 0 (Serial Clock Polarity) //Bits 5,4: FRF = 0 (Frame Format = SPI)
//Enables
SSI0_CR1_R = ENABLE_SSI; //Enables SSI operation again } /********************************************************************************************** ******************/ //* Function: ADC_Interruprion_Compare //* Task: Sets pin 6 from GPIO A as a rising edge triggered interrupt. /********************************************************************************************** ******************/ extern void ADCIC (void) { //Code GPIO_PORTA_ICR_R = (PIN7 | PIN6 | PIN5 | PIN4 | PIN3 | PIN2 | PIN1 | PIN0); //Clear all edge interruptions GPIO_PORTA_DEN_R = PIN6; //Set pin 6 as input GPIO_PORTA_IS_R = ALL_EDGE_SENSITIVE; //Set edge-sensitive interruptions GPIO_PORTA_IBE_R = CONTROLLED_BY_INTERRUPT_EVENT; //Interrupt generation controlled by the Interrupt Event (GPIOIEV) GPIO_PORTA_IEV_R = FALLING_EDGE_INTERRUPTIONS; ` //Configure pin 6 to trigger an interruption when falling edges GPIO_PORTA_IM_R = PIN6; //Interruptions coming from pin 6 are allowed GPIO_PORTA_ICR_R = (PIN7 | PIN6 | PIN5 | PIN4 | PIN3 | PIN2 | PIN1 | PIN0); //Clear all interruptions } /********************************************************************************************** ******************/ //* Function: ADC_SSI_Send8 //* Task: Sends 8 bits data via SPI. First, it configures the SPI channel to a data size of 8 bits. // Then, it sends the data. /********************************************************************************************** ******************/ extern void ADC_SSI_Send8(long unsigned int data_to_be_sent) { //Code GPIO_PORTA_ICR_R = PIN6; //Clear interruptions from pin 6 (void) SSIDataPut(SSI_BASE, data_to_be_sent); //Sends the information to the ADC } /********************************************************************************************** ******************/ //* Function: ADC_Calibration //* Task: The output SYNC_ADC is set to 0 during 100 ms in order to calibrate the ADC, then is set again to 1. /********************************************************************************************** ******************/
89
extern void ADC_Calibration (void) { //Code GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_7, !GPIO_PIN_7); //We set the SYNC pin to 0 SysCtlDelay (SYNC_TIME_100MS); //Waits for synchronisation with the ADC (100 ms) GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_7, GPIO_PIN_7); //Once the synchronisation has been done, we set SYNC to 1 } /********************************************************************************************** ******************/ //* Function: InterruptSSIADCControl //* Task: Handler used to manage the interruptions coming from the SSI used by the ADC. // The handler can manage 3 different interruptions: // - Recieve FIFO: When the RX FIFO is half full or more. We read the FIFO, update the ADC counter and check if // a whole package has been received(24 bits). // - Time-Out: As the data coming from the ADC will never be enough to trigger the Receive FIFO interruption, // the Time-Out interruption is needed. This interruption is triggered whenever there is data inside the RX // which has not been read. It performs the same tasks as the Receive FIFO, but with a delay of 32 bits // - Overrun: This function can be implemented for the SPI, but it is of no use in the pesent code /********************************************************************************************** ******************/ void InterruptSSIADCControl (void) { //Local variables unsigned long adc_ssi_interruption_status; //Code adc_ssi_interruption_status = SSI0_MIS_R; if((adc_ssi_interruption_status & SSI_ADC_RECEIVE_FIFO)) { ADC_received_data [adc_counter] = SSI0_DR_R; //We read the data coming from the ADC and store it in the memory adc_counter++; //Update the adc_counter (counts the packets of 8 bits received from the ADC) if (adc_counter == HAVE_24_BITS_BEEN_READ) { adc_counter = 0; //We reset the counter of the ADC } else { (void)ADC_SSI_Send8(dummy_write); } } if((adc_ssi_interruption_status & SSI_ADC_TIME_OUT)) { ADC_received_data [adc_counter] = SSI0_DR_R; adc_counter++; if (adc_counter == HAVE_24_BITS_BEEN_READ) { adc_counter = 0; } else { (void)ADC_SSI_Send8(dummy_write); } } if((adc_ssi_interruption_status & SSI_ADC_OVERRUN)) //TODO { } }
// ================================================== // End //-------------------------------------------------------// Project ........................... MuZIC // Module ............................ // File .............................. drv_spi0_adc.h //-------------------------------------------------------// Description ....................... // // Tasks: //-------------------------------------------------------// (c) Copyright Schlumberger 2009 - All rights reserved //-------------------------------------------------------#ifndef _DRV_SPI_FPGA_H_ #define _DRV_SPI_FPGA_H_ //-------------------------------------------------------// Glocal Define, macro //-------------------------------------------------------//SSI Configuration #define DISABLE_SSI #define ENABLE_SSI #define CLOCK_PRESCALE_2 number between 2 and 256) #define INT_RX_FIFO #define INT_TIME_OUT #define INT_OVERRUN #define SSI_CLK #define SSI_CS #define SSI_RX #define SSI_TX #define ADC_SYNC #define ADC_DRDY #define ANALOG_COMPARATOR_0 #define SSI_BASE #define #define #define #define #define #define #define SCR SPH SPO SPH FRF DSS_8 DSS_16
0x0000 0x0002 0x0002 (1<<2) (1<<1) (1) GPIO_PIN_2 GPIO_PIN_3 GPIO_PIN_4 GPIO_PIN_5 GPIO_PIN_6 GPIO_PIN_7 0 0x40008000 // SSI_BASE is the address of the SSI transmit FIFO (2<<8) (0<<7) (0<<6) (0<<7) (0<<4) (0x7) (0xf) 8 16 //SCR = 2 //SPH = 0 //SPO = 0 //SPH = 0 //Freescale SPI Format //Data Size Select = 8 //Data Size Select = 16 //Set prescale to 2 (has to be an even //Enable RX FIFO interrupt //Enable Time out interrupt //Enable Overrun interrupt
#define IS_SPI_BUSY 0x0010 //If the SPI is busy, or the transmit FIFO is not empty, bit 4 SSISR register is set to 1
91
0x04
#define DUMMY_WRITE 0xaa #define SYNC_TIME_100MS 427000 #define HAVE_24_BITS_BEEN_READ 2 //-----------------------------------------------------------------// Generic API //-----------------------------------------------------------------extern void ADC_SSI_Configure (void); extern void ADCIC (void); extern void ADC_Calibration (void); extern void ADC_SSI_Send8(long unsigned int data_to_be_sent); extern void InterruptIC(void) ; extern void InterruptSSIADCControl (void); #endif
// ================================================== // End //-------------------------------------------------------// Project ........................... MuZIC // Module ............................ // File .............................. system_config.c //-------------------------------------------------------// Description ....................... // // Tasks: //-------------------------------------------------------// (c) Copyright Schlumberger 2009 - All rights reserved //-------------------------------------------------------/****************************************************************************************/ //INCLUDE /****************************************************************************************/ #include "hw_ints.h" #include "hw_memmap.h" #include "hw_types.h" #include "debug.h" #include "gpio.h" #include "interrupt.h" #include "ssi.h" #include "sysctl.h" #include "lm3s2965.h" #include "timer.h" #include "system_config.h" /*****************************************************************************************/
/****************************************************************************************/ //* Function: SYS_Configuration //* Task: Configures the system clock and the peripherials which will be used /****************************************************************************************/ extern void SYS_Configuration (void) { SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN); SYSCTL_RCGC2_R |= (PORTG | PORTA); SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG); //Activates the use of Port A and G
/********************************************************************************************** *******************/ //END /********************************************************************************************** *******************/ //-------------------------------------------------------// Project ........................... MuZIC // Module ............................ // File .............................. system_config.h //-------------------------------------------------------// Description what this module is used for....................... // // Tasks: //-------------------------------------------------------// (c) Copyright Schlumberger 2009 - All rights reserved //--------------------------------------------------------
93
#define #define #define #define #define #define #define #define //Ports #define #define #define #define #define #define #define #define
//System activation of the interruptions #define INT_UART0 21 #define INT_GPIOA 16 #define INT_SSI0 23 //Timer 0 define #define COUNTER_20_MS 525
/********************************************************************************************** *******************/ //GENERIC API /********************************************************************************************** *******************/ extern void SYS_Enable_Interruptions (void); extern void SYS_Configuration (void); extern void SYS_Timer_Configuration (void);
4. PWM
#include #include #include #include #include #include #include #include #include #include #include "hw_ints.h" "hw_memmap.h" "hw_types.h" "debug.h" "gpio.h" "interrupt.h" "sysctl.h" "lm3s2965.h" "system_config.h" "drv_pwm.h" "pwm.h"
int main(void) { SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN); clocking to run directly from the crystal. // Set the
} /********************************************************************************************** *******************/ //END /********************************************************************************************** *******************/ //-------------------------------------------------------// Project ........................... MuZIC // Module ............................ // File .............................. drv_pwm.c //-------------------------------------------------------// Description ....................... // // Tasks: //-------------------------------------------------------// (c) Copyright Schlumberger 2009 - All rights reserved //--------------------------------------------------------
//-------------------------------------------------------// Include //-------------------------------------------------------#include "hw_ints.h" #include "hw_memmap.h" #include "hw_types.h" #include "debug.h" #include "gpio.h" #include "interrupt.h" #include "sysctl.h" #include "lm3s2965.h" #include "drv_pwm.h" #include "system_config.h" #include "pwm.h" //-------------------------------------------------------// Local Define, macro //--------------------------------------------------------
//-------------------------------------------------------// Local enum, struct, union //-------------------------------------------------------//-------------------------------------------------------// Local type //-------------------------------------------------------//-------------------------------------------------------// Local constants, variables //-------------------------------------------------------int index_sinewave5000Hz = 0; //-------------------------------------------------------// Global constants, variables //-------------------------------------------------------static long Sinewave5000Hz [SINEWAVE_TABLE_SIZE] = {118,115,108,96,81,64,47,32,20,13,10,13,20,32,47,64,81,96,108,115}; //--------------------------------------------------------
95
// Local functions //-------------------------------------------------------/********************************************************************************************** ******************/ //* Function: PWM_Configure //* Task: Configures the hardware in order to use the PWM 0. //* The configured PWM has the following features: //* - Provided a the microprocessor uses a 12.8MHz clock, the PWM0 works with a 12.8 MHz clock //* - The Timer Mode used is Count-Down (from the Loaded value to zero) //* - The Dead-Band Generator is used to create the second signal. Therefore, the second signal is not independent. //* - Both Rising Edge Delay and falling Edge Delay of the Dead-Band Generator are the same (5/12.8MHz = 380ns) (see page 490, LM3S2965 Data Sheet) //* - The PWM generates a signal every 100KHz //* - No synchronisation mode is used /********************************************************************************************** ******************/ void PWM_Configure(void) { //Code SysCtlPWMClockSet(SYSCTL_PWMDIV_1); //Sets the PWM Clock Predivider: We directly take the System Clock SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM); Enable the peripherals used by the PWM 0. (PWO and GPIO G) SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG); GPIOPinTypePWM(GPIO_PORTG_BASE, GPIO_PIN_2 | GPIO_PIN_3); G3 as PWM pins. They are used to output the PWM0 and PWM1 signals. //
PWMGenConfigure(PWM_BASE, PWM_GEN_0, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_NO_SYNC); Configuration PWMGenPeriodSet(PWM_BASE, PWM_GEN_0, FREQUENCY_PWM_100KHZ); PWMDeadBandEnable(PWM_BASE, PWM_OUT_0,WIDTH_DELAY_DEAD_BAND_RISING_EDGE, WIDTH_DELAY_DEAD_BAND_FALLING_EDGE); //Enables Dead Band PWMPulseWidthSet(PWM_BASE, PWM_OUT_0, PWM_0_DEFAULT_VALUE); default value PWMGenIntTrigEnable(PWM_BASE, PWM_GEN_0,PWM_INT_CNT_ZERO); interruptions from PWM Generator Block 0 PWMIntEnable(PWM_BASE, PWM_INT_GEN_0); //Enables generator and fault interruptions for PWM Module 0 IntEnable(INT_PWO_0); //Enable interruptions coming from PWM 0
//PWM
//Enable
PWMOutputState(PWM_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT, true); // Enable the PWM0 and PWM1 output signals. PWMGenEnable(PWM_BASE, PWM_GEN_0); // Enable the PWM generator. } /********************************************************************************************** ******************/ //* Function: InterruptPWM0 //* Task: Handler for the PWM 0 interruption. //* Every time the PWM 0 Counter reaches 0 (10 micro seconds), an interruption is triggered. //* This interruption clears the interruption flag, takes a value from a table, and loads it into the PWM0. /********************************************************************************************** ******************/ void InterruptPWM0(void) { //Variables unsigned long data;
if (index_sinewave5000Hz == SINEWAVE_TABLE_SIZE) //If we arrive to the end of the table, we reset the index table { index_sinewave5000Hz = 0; } } // ================================================== // End //-------------------------------------------------------// Project ........................... MuZIC // Module ............................ // File .............................. drv_pwm.h //-------------------------------------------------------// Description ....................... // // Tasks: //-------------------------------------------------------// (c) Copyright Schlumberger 2009 - All rights reserved //-------------------------------------------------------#ifndef _DRV_PWM_H_ #define _DRV_PWM_H_ //-------------------------------------------------------// Glocal Define, macro //-------------------------------------------------------#define FREQUENCY_PWM_100KHZ 128 #define WIDTH_DELAY_DEAD_BAND_RISING_EDGE 5 #define WIDTH_DELAY_DEAD_BAND_FALLING_EDGE 5 #define INT_PWO_0 26 #define PWM_0_DEFAULT_VALUE 32 #define CLEAR_PWM_0_INTERRUPTION 0x01
#define SINEWAVE_TABLE_SIZE
20
//-----------------------------------------------------------------// Generic API //-----------------------------------------------------------------extern void PWM_Configure(void); extern void InterruptPWM0(void); #endif
// ================================================== // End //-------------------------------------------------------// Project ........................... MuZIC // Module ............................ // File .............................. system_config.c //-------------------------------------------------------//-------------------------------------------------------// (c) Copyright Schlumberger 2009 - All rights reserved
97
//-------------------------------------------------------/***************************************************************************************/ //ISSUES /***************************************************************************************/ //* This file contains all the functions that need to be called before using the // microprocessor. //* All the functions related with the configuration of the system are placed here //* Interruptions must be enabled AFTER the configuration of the system and all the // peripherials. Otherwise, the program will not function properly. /***************************************************************************************/ /****************************************************************************************/ //INCLUDE /****************************************************************************************/ #include "hw_ints.h" #include "hw_memmap.h" #include "hw_types.h" #include "debug.h" #include "gpio.h" #include "interrupt.h" #include "ssi.h" #include "sysctl.h" #include "lm3s2965.h" #include "timer.h" #include "system_config.h" /*****************************************************************************************/
/****************************************************************************************/ //* Function: SYS_Configuration //* Task: Configures the system clock and the peripherials which will be used /****************************************************************************************/ extern void SYS_Configuration (void) { SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN); SYSCTL_RCGC2_R |= (PORTG | PORTA | PORTH); A and G SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG); GPIO_PORTG_DIR_R = (PIN2); GPIO_PORTG_DEN_R = (PIN2); } /****************************************************************************************/ //* Function: SYS_Activate_Interruptions //* Task: Enables the use of interruptions by the system, and enables the interruptions // of the peripherials in use /****************************************************************************************/ void SYS_Enable_Interruptions (void) { IntMasterEnable(); } /****************************************************************************************/ //* Function: SYS_Timer_Configuration //* Task: Configures the Timer 0 of the microprocessor. The Timer counts backwards starting // from a number set by the user. Once the Timer has been configured, it starts to count. /****************************************************************************************/ extern void SYS_Timer_Configuration (void) { SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0); //Enables the peripherial TimerConfigure(TIMER0_BASE, TIMER_CFG_32_BIT_PER); //Chooses the 32-bit, periodic counter TimerLoadSet(TIMER0_BASE, TIMER_A, COUNTER_20_MS); //Sets the timer to count 20ms //Configuration of the pin used by the LED //Activates the use of Port
/*********************************************************************************************/ //END /*********************************************************************************************/ //-------------------------------------------------------// Project ........................... MuZIC // Module ............................ // File .............................. system_config.h //-------------------------------------------------------//-------------------------------------------------------// (c) Copyright Schlumberger 2009 - All rights reserved //--------------------------------------------------------
/*********************************************************************************************/ //GLOBAL DEFINE, MACROS /*********************************************************************************************/ //Pins #define PIN0 0x01 #define PIN1 0x02 #define PIN2 0x04 #define PIN3 0x08 #define PIN4 0x10 #define PIN5 0x20 #define PIN6 0x40 #define PIN7 0x80 //Ports #define #define #define #define #define #define #define #define
//System activation of the interruptions #define INT_UART0 21 #define INT_GPIOA 16 #define INT_SSI0 23 //Timer 0 define #define COUNTER_20_MS 525 /*********************************************************************************************/ //GENERIC API /*********************************************************************************************/ extern void SYS_Enable_Interruptions (void); extern void SYS_Configuration (void);
99
Figure 38 Mapping the ocean layer To continue the search and to be able to view oil and gas reservoirs that are buried under thousands of meters of sea or rock, seismic surveys are executed. They can be performed on land or at sea but the principles are the same: sound waves penetrate the many layers of rocks. When one layer meets another at a boundary, the waves are reflected. Each boundary reflects a part of the sound back to the surface. The rest continues downwards. On the surface, special devices geophones pick up the reflected sounds. Depending on how long the reflection time is, the type of geological formation can be inferred.
2. Well construction
The construction of a well is executed in several steps. First, the well is drilled and logged. Then, casing is installed and cementing carried out. Since drilling, logging, casing, and cementing are performed step-by-step, they are repeated several times. After that, production tubing and packers are installed. Finally, perforations are done in the casing to permit the oil to flow into the tubing.
3. Drilling
The majority of the rigs today are rotary drilling rigs. One advantage is that the rotary drill can drill in most formations. The rotary drilling rig uses a rotary bit with rows of teeth that penetrate the rock and scrape the rock out. Afterwards, the cuttings of rock must be moved out of the way. Otherwise, the drill bit would be hindered. To permit this, fluid circulates in the well. This fluid, called drilling mud, transports the cuttings to the surface where they are sorted out so that the drilling mud can be recycled in the borehole. The fluid enters the well through the drill pipe and goes out through the drill bit. A huge pump on the surface moves the mud circulation system.
101
Drilling mud is not just simple mud, but a complex mixture of different materials. A better name for it would be therefore drilling fluid. The fluid also prevents the well from collapsing, as the whole borehole is filled with fluid that supports the walls. Another function of the drilling fluid is the cooling effect it has on the drilling bit.
Figure 40 - Drilling
4. Logging
Logging is a set of techniques which consists of collecting and registering in real time geological information about the interior of the Earth. The logging enables eventually to confirm the surveys that have been previously done. Only one out of seven exploration wells is developed into a production well. Much has occurred since the Schlumberger brothers succeeded with the first electric log in 1927. Today, there are many different variants, each with its own specialty and field of application. The main types are: Wireline logging and logging while drilling (LWD). Wireline logging is used in open and cased holes. LWD is performed, as its name indicates, while drilling.
Figure 41 A Wireline Tool Logging is performed from a portable laboratory. It is placed in a truck for land rigs and in a portable logging cabin offshore. To execute the logging, tools are lowered into the well on a wireline. After reaching the bottom, they are slowly reeled back up. Formation properties are measured during the rising. The tools transmit the data instantly to computers in the laboratory. The data is processed by computers in real-time and interpreted by experts. The gathered data is displayed in logs that indicate the presence of oil and gas. Logging can also be performed while drilling. This method is called LWD. This gives the operators valuable information while drilling so that the drilling can be adapted more accurately to different rock formations. All the gathered information is transmitted to the surface in order to be analyzed. A wireline log has to be performed after the drilling anyway to complete the logging.
103
5. Casing
To prevent the well from collapsing and to isolate the rock formations from the well, the well has to be cased. The setting of the casing is done in several steps. A well of several hundred meters cannot be drilled in one step; it has to be stabilized regularly as the drilling goes deeper. After the casing has been set, the initial hole becomes smaller. A smaller bit, which fits in the new hole, is used. This means that the wells diameter becomes progressively smaller as the well deepens.
6. Cementing
The objectives of cementing are to provide a complete isolation of different zones, support the casing, and protect the casing string. It is very important that fluids cannot migrate from one formation to another. This is to prevent, for example, oil leakage in nearby water reservoirs. Cement contains silica, alumina and iron oxide. Cement wells sets through a chemical process that does not require air. This process consists of very complex chemistry.
105