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

Practical Arduino Cool Projects for Open Source Hardware- P23 pptx

10 421 0

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

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 10
Dung lượng 167,36 KB

Nội dung

CHAPTER 11  OSCILLOSCOPE/LOGIC ANALYZER To send a set of eight 10-bit values plus seven separator characters and a newline (CRLF) at the end requires between 17 and 41 bytes of data to be transmitted via the USB connection depending on the specific values. This limits it to sending to around 350 samples/second on average with the serial connection set to 115200bps. Later examples attempt to overcome some of these limitations. First, the program defines the cbi() and sbi() macros so we can use them to change the prescaler values. #ifndef cbi #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) #endif #ifndef sbi #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) #endif Then the program defines a variable that will be used to store each reading as it is taken. int val; The setup() function sets the prescaler to 16 as described previously, then sets digital pins 6 and 7 to input mode and opens the connection to the host at 115200bps. void setup() { sbi(ADCSRA,ADPS2); cbi(ADCSRA,ADPS1); cbi(ADCSRA,ADPS0); pinMode(6, INPUT); pinMode(7, INPUT); Serial.begin(115200); } The program loops as fast as possible, starting by taking a reading from each analog input starting at input 0 and counting up to analog input 5. The value from each input is sent straight to the host as soon as it is read, with a space character to separate them. void loop() { for( int i=0; i<6; i++ ){ Serial.print( analogRead(i) ); Serial.print(" "); } Because probes 6 and 7 are connected to digital inputs we have to fudge the values. If the reading is low, we return a value of 0; if it's high, we return a value of 1023. That way high and low values show the same range as the analog inputs, even though they'll never show an intermediate value like an analog input. Note that the values returned for input 6 have a space appended as a separator for the next value. if( digitalRead(6) == 1) { Serial.print(1023); Serial.print(' '); } else { Serial.print(0); 199 CHAPTER 11  OSCILLOSCOPE/LOGIC ANALYZER Serial.print(' '); } if( digitalRead(7) == 1) { Serial.print(1023); } else { Serial.print(0); } Once all the samples have been sent to the host the program sends a carriage return so the program running on the host knows it's reached the end of a line and needs to process it. Serial.println(); } Digital Read Version A normal analog read takes over 100 microseconds and even our high-speed version takes about 16 microseconds, but a digital read can be accomplished in under 1 microsecond. It's therefore preferable to use digital reads if you don't care about the shape of the waveform and only want to know if a pin you are testing is high or low. This version of the program operates in the same basic manner as the previous version and returns data in the same space-separated format with one value per input probe so that it's compatible with the same host software, but it samples much faster by doing only digital reads rather than analog reads. It's also optimized by using direct port manipulation rather than traditional Arduino-style calls to functions like digitalRead. The reason is that a simple function call like digitalRead(6) actually takes quite a few clock cycles to execute because what it does behind the scenes is: 1. Look up the Arduino pin number and convert it to the equivalent ATMega port number and pin number. 2. Disable any PWM (pulse-width modulation) functionality that might be running on that pin. 3. Read the pin. Even after compilation this requires several dozen processor cycles just to do a simple digital read. There is work currently being performed to optimize digital read and write performance for a future release of Arduino, but at the time of writing they are fairly expensive operations in terms of processor time. By addressing the appropriate ATMega port directly it's possible to perform a digital read in only a few cycles. This makes the code harder to read so it's not recommended for general Arduino projects, but in cases like this where we're squeezing every last bit of performance out of the processor it makes perfect sense. As in the previous version the program starts by setting up the input pins, but because we're using analog inputs 0 through 5 in digital mode we address them using their alternative pin numbers as digital pins 14 through 19. Because the setup process isn't time critical we use the regular Arduino pinMode() function to configure the inputs rather than using direct port manipulation as in the main program loop. Digital pins 6 and 7 are also set to input mode in the setup function and the connection to the host is opened at 115200bps. 200 CHAPTER 11  OSCILLOSCOPE/LOGIC ANALYZER void setup() { pinMode(14, INPUT); pinMode(15, INPUT); pinMode(16, INPUT); pinMode(17, INPUT); pinMode(18, INPUT); pinMode(19, INPUT); pinMode(6, INPUT); pinMode(7, INPUT); Serial.begin(115200); } The main program loop is where things get interesting. Rather than a series of lines something like "Serial.print(digitalRead(14));", it instead directly accesses the port registers and uses a bitmask to hide all the pins we're not interested in. The ATMega328P used in many Arduino models including the Duemilanove has three registers for each of the three ports exposed on an Arduino as I/O lines, with one register setting the direction (input or output) of each pin, one register storing the last value written to each pin, and one register returning the result of a digital read on each pin. Port D is exposed as Arduino digital pins 0 through 7. • DDRD: The port D Data Direction Register. • PORTD: The port D Data Register. • PIND: The port D Input Pins Register. Port B is exposed as Arduino digital pins 8 through 13. The reason this isn't 8 through 15 is that the two high bits are used by the crystal that provides the Arduino with its clock source, so they're not available on the board as I/O lines. • DDRB: The port B Data Direction Register. • PORTB: The port B Data Register. • PINB: The port B Input Pins Register. Port C is exposed as Arduino analog pins 0 through 5. Once again the top two bits aren't usable in most Arduino designs because even though those pins exist on the CPU they simply aren't brought out on the Arduino's expansion headers. • DDRC: The port C Data Direction Register. • PORTC: The port C Data Register. • PINC: The port C Input Pins Register The loop takes the value of the appropriate input pins register (either PINC for the analog inputs or PIND for the digital inputs) and applies a bitmask before sending the result to the host. The & (logical AND) operator used to apply the bitmask takes two binary numbers and returns a number that only has bits set high where both the numbers have the corresponding bit set high. For example, the result of B00001111 & B11000011 201 CHAPTER 11  OSCILLOSCOPE/LOGIC ANALYZER would be B00000011, because only the last two bits were set high in both numbers. By applying a sequence of bitmasks to the PINC and PIND values we can isolate the value of each pin and check whether it is low or high. However, just applying the bitmask doesn't do everything we need because the value returned won't be just 0 for low or 1 for high except in the case of the very first pin. Reading the second pin would return 0 for low and 2 for high, while the third pin would return 0 or 4, the fourth 0 or 8, and so on. So after applying the bitmask we apply a bit shift right operator to move the bit we're interested in a certain number of positions to the right. That way, a value such as decimal 8 obtained by reading a high value on input 4 will be converted to a value of decimal 1, and the program will only ever return a 0 or a 1 value for any input pin depending on its state. void loop() { Serial.print( PINC & B00000001 ); Serial.print(" "); Serial.print( (PINC & B00000010) >> 1 ); Serial.print(" "); Serial.print( (PINC & B00000100) >> 2 ); Serial.print(" "); Serial.print( (PINC & B00001000) >> 3 ); Serial.print(" "); Serial.print( (PINC & B00010000) >> 4 ); Serial.print(" "); Serial.print( (PINC & B00100000) >> 5 ); Serial.print(" "); Serial.print( (PIND & B01000000) >> 6 ); Serial.print(" "); Serial.print( (PIND & B10000000) >> 7 ); Just as in the previous version, once all the samples have been sent to the host the program sends a carriage return so the program running on the host knows it's reached the end of a line and needs to process it. Serial.println(); } Digital Read Sketch with Optimized Communications Format The previous digital read sketch has a huge inefficiency in the way it communicates with the host, but it's one that was introduced deliberately to maintain compatibility with host-side software for processing the data and displaying it. There are several projects documented online to use an Arduino as an oscilloscope or logic analyzer and they generally use a data format consisting of individual readings from each pin transmitted as one or more bytes for each pin separated by spaces. That makes sense if you're dealing with analog values, but if all you care about is digital values it's a highly inefficient way of transferring data. Because the serial connection to the host is a major bottleneck that dramatically slows down the sample rate it's worth optimizing the data format to pump as much information through as possible in the smallest number of bytes. If you want to represent the state of eight digital inputs simultaneously, the absolute minimum amount of data you can use is one byte, or eight bits. Each bit corresponds to the state of one of the inputs. 202 CHAPTER 11  OSCILLOSCOPE/LOGIC ANALYZER This version of the sketch is, therefore, designed to be incredibly fast and return just one byte of data per sample to represent the state of all eight inputs simultaneously. We even dispensed with the carriage return by making the host-side program assume that every byte received is one complete reading of eight inputs. That assumption only works because we're limiting ourselves to eight inputs, of course, but if you wanted to sample more inputs you could add a second data byte to represent inputs 8 through 15 and append a carriage return to signify the end of a sample to the host. The program starts off just like the last one, setting up the pins and communications in the exact same way. void setup() { pinMode(14, INPUT); pinMode(15, INPUT); pinMode(16, INPUT); pinMode(17, INPUT); pinMode(18, INPUT); pinMode(19, INPUT); pinMode(6, INPUT); pinMode(7, INPUT); Serial.begin(115200); } The difference is in the main program loop, which is actually simpler than the previous version because we don't have to muck around with sampling each bit individually and then bit-shifting the result. Instead, we read the PINC and PIND registers just once each, apply a bitmask to each of them to ignore the top two bits on PINC and the bottom six bits on PIND, and use a bitwise "OR" operator (the | symbol) to combine them into a single byte before sending the result back to the host. A bitwise OR operator combines two values by setting the resulting bit high if the corresponding bit is set high in either of the original values. For example, the result of B11001100 | B00001111 would be B11001111 because the first two bits and the last four bits are all set to high in either or both of the original values. Using the bitmask to select only the top two bits from PIND and the bottom six bits from PINC allows us to combine the two partial bytes into a single complete byte. void loop() { Serial.print( (PIND & B11000000) | (PINC & B00111111) ); } And that's it. All the magic is combined into that single optimized line, with the result that this version of the program will sample all eight digital inputs in under two microseconds and return the result as a single byte of data representing the current state of all eight inputs. It doesn't get much more optimized than that. Note, though, that this format won't work with the Arduinoscope program described next unless you modify the Arduinoscope data parser. Install Processing and Run Visualization Program In this project the Arduino does very little: it just samples its inputs and ships the values off to a host connected via USB as fast as possible. All the intelligence is in the program you run on the host. 203 CHAPTER 11  OSCILLOSCOPE/LOGIC ANALYZER There are several projects online for Arduino-based oscilloscopes, but one of the most well developed is Arduinoscope. All the code for Arduinoscope is published on Google Code for download. See code.google.com/p/arduinoscope/.We've also provided handy links on the project page at the Practical Arduino web site. The Arduinoscope project consists of two parts: the sketch that runs on the Arduino, and the program that runs on the host. The first example sketch listed previously works in a very similar way to the sketch included in Arduinoscope, and returns its data in the same format with values separated by spaces and samples separated by newlines. The only difference is that we've added the ADC prescaler to make the conversion process run as fast as possible and tuned the pin selections to match the ones we wired up. If you prefer to run the Arduinoscope version of the program it will work just fine on the hardware we describe here, but depending on which inputs you wired up they may not match the channels in the program. The project page on Google Code includes downloads for prepackaged versions for Windows, Linux, and Mac OS. The simplest approach is to just download a package and run it. There are also source downloads available if you're interested in modifying it to suit your requirements. After downloading the appropriate version, open the folder and launch the program inside it to be presented with a display similar to the one shown in Figure 11-11. Figure 11-11. Arduinoscope program running on a host connected to an Arduino 204 CHAPTER 11  OSCILLOSCOPE/LOGIC ANALYZER By default Arduinoscope is configured to display six input channels. With our hardware and the sampling program previously shown, the first six channels correspond to the analog inputs, so this is ideal if you want to read from analog circuits or see the shape of digital waveforms. Arduinoscope supports a few different modes. The default mode simply shows the shape of the waveform being measured, but if you only care about the logical state (HIGH or LOW) of a line, you can click the LOGIC button in the top right for that channel. It will then highlight the entire channel in either red or green depending on whether the input is high or low, providing very quick visual feedback. You can also pause individual channels and save the data to disk as a CSV file, so experiment with it a bit to learn about all the things it can do. And if you want to write your own variation you'll discover that the code has been neatly organized into a "scope" class that you can use in your own projects. Note, though, that it won't work as-is with the "digital" version of the sampling program described previously because it expects values to be returned in the range 0 to 1023, while the digital sampler only ever returns values of 0 or 1. Arduinoscope will faithfully report either 0V or 0.005V (1/1023 of 5V) depending on the state of the input but the logic mode won't work because the input will never pass the threshold for a high value. Variations Input Voltage Prescaler Arduino inputs can read values between 0V (ground) and the operating voltage of the CPU, which in the case of most Arduino designs is 5V. Some Arduinos run at 3.3V but they are the exception rather than the rule. What this means for an Arduino-based oscilloscope or logic analyzer is that it can only be connected to circuits that run in that same voltage range. If you run in analog input mode and try plotting the waveform of an audio signal running through an amplifier at 36V, you'll probably blow the Arduino input if not the whole chip. Conversely, if you run in digital input mode and try to read the status of data lines on a 3.3V or 2.6V circuit, it might not be able to read a HIGH value because the voltage level of the system under test isn't high enough for the Arduino to read. The solution to these problems is to apply a prescaler in the probe itself before the level is sent through to the Arduino for measurement. Scaling down a high voltage to a safe level for the Arduino to read is quite simple using a voltage divider formed from two or more resistors. The principle of a voltage divider is that if you place a voltage across several resistors in series, the voltage at the points between the resistors will be a fraction of the total voltage. This can be demonstrated by building a special input probe that uses a voltage divider connected between the probe input and the shield (ground) connection to bias one end of a divider network to 0V, with the other end of the network connected to the probe tip and the middle point connected to the probe input on the Arduino oscilloscope (see Figure 11-12). Figure 11-12. 2:1 Voltage divider probe 205 CHAPTER 11  OSCILLOSCOPE/LOGIC ANALYZER If the probe is connected to a part of the circuit under test that happens to be at 10V, the full voltage will be seen at the start of the voltage divider, while 0V will be seen at the end thanks to the connection to ground. The matching pair of 10K resistors cause the voltage at the center point to be divided in half, presenting 5V to the Arduino input. Using a pair of resistors of equal value gives a 2:1 voltage divider, but using different values allows you to change the division ratio. For example, a voltage divider with 40K at the top and 10K at the bottom would put the voltage at the division point at 1/5 of the total voltage. This would allow you to connect the probe to circuits running at up to 25V while still only presenting up to 5V to the Arduino input. Because of the way resistor ranges are scaled it's not actually possible to buy a 40K resistor, so one handy technique is to build a prescaler out of a resistor ladder consisting of multiples of the same value of resistor connected in series. You can even add a button or switch to let you select the prescale value, giving you a probe that can be altered depending on what you are trying to measure. Scaling voltage up, on the other hand, is much trickier without altering the operation of the circuit under test. One of the basic principles of test equipment is not to alter the behavior of the circuit while you are measuring it, because otherwise your measurements could be invalid. An important concept in testing is input impedance, which is the effective resistance of the test equipment input as seen by the circuit under test. If the input has low impedance and is biased low, it will tend to pull the part of the circuit being measured down to a low voltage, and if it's biased high it will tend to pull it to a high voltage. Having a very high input impedance (typically in the region of several million ohms) is highly desirable because it minimizes the impact on the circuit under test. Feeding the probe input to a transistor via a limiting resistor will allow the signal to be amplified, but could actually alter the characteristics of the circuit by presenting an input impedance that is too low and drawing too much current into the transistor. What is needed is an amplifier with a very linear response across its entire range and a very high input impedance. A circuit based around an op-amp might fit the bill, but since it's not a typical requirement for the sorts of circuits used with an Arduino it won't be covered here. A simple, low-performance solution to the problem that would work with the hardware described in this project unchanged is to use analog inputs and apply a scaling factor to the readings. This would cause you to lose some resolution because only part of the input range would be exercised, but it may be acceptable in some situations. It's also possible to change the ADC input range by changing the analog reference voltage. More information on this technique is available on the Arduino site at: www.arduino.cc/en/Reference/AnalogReference Resources There are a number of different Arduino-based oscilloscope and logic analyzer projects, and they should all work with the hardware described in this project simply by changing the program on the Arduino and the program you run on the host. Experiment with a few and find the one that suits your needs best. Poor Man's Oscilloscope: accrochages.drone.ws/en/node/90 Arduinoscope: code.google.com/p/arduinoscope/ Macduinoscope: www.gabuku.com/scopeAVR Logic Analyzer: www.uchobby.com/index.php/2008/09/09/avr-logic-analyzer/ The Arduino site has some useful pages explaining how the analog inputs work as well as background on the direct port manipulation we used in the digital sampler program in this project. www.arduino.cc/en/Tutorial/AnalogInputPins www.arduino.cc/en/Reference/PortManipulation 206 CHAPTER 11  OSCILLOSCOPE/LOGIC ANALYZER 207 If you want to learn a bit more about oscilloscopes and logic analyzers in general, there are good pages on Wikipedia about both topics. en.wikipedia.org/wiki/Logic_analyzer en.wikipedia.org/wiki/Oscilloscope . There are several projects online for Arduino- based oscilloscopes, but one of the most well developed is Arduinoscope. All the code for Arduinoscope is published on Google Code for download. See. code.google.com/p/arduinoscope/.We've also provided handy links on the project page at the Practical Arduino web site. The Arduinoscope project consists of two parts: the sketch that runs on the Arduino, . isn't high enough for the Arduino to read. The solution to these problems is to apply a prescaler in the probe itself before the level is sent through to the Arduino for measurement. Scaling

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

TỪ KHÓA LIÊN QUAN