Updated: October 28, 2024 |
The pcl711_read_analog() function is used to handle the analog input:
#define PCL711_DELAY 1000 // 1 us int pcl711_read_analog (pcl711_t *pcl, int channel) { int data, base, timeout; static int calibrated = 0; static struct timespec when; // 1) calibrate nanospin if required if (!calibrated) { nanospin_calibrate (1); // with interrupts off nsec2timespec (&when, PCL711_DELAY); calibrated = 1; } // 2) ensure we are in range channel &= 7; base = pcl -> port; // 3) select the channel out8 (base + PCL711_MUX_SCAN_CONTROL, channel); // 4) select the gain out8 (base + PCL711_GAIN_CONTROL, pcl -> gains [channel]); // 5) trigger the conversion out8 (base + PCL711_SOFTWARE_AD_TRIGGER, 0 /* any data */); // 6) wait for the conversion timeout = PCL711_TIMEOUT; do { data = in8 (base + PCL711_ANALOG_HIGH); nanospin (&when); // spin } while ((data & PCL711_ANALOG_HIGH_DRDY) && (timeout-- >= 0)); // 7) indicate timeout if any if (timeout < 0) { return (-1); } // 8) return data data = ((data & 0x0f) << 8) + in8 (base + PCL711_ANALOG_LOW); return (data); }
The code performs the following steps:
Notice that the first parameter, pcl, is of type pointer to pcl711_t. The pcl711_t is the extended attributes structure used in the resource manager. It's a convenient place to store additional information, such as the base port address.
Notice that in step 6 we are polling. While polling is generally frowned upon in realtime control systems, we have no choice. The manual states that the conversion will take place within microseconds, so the overhead of giving up the CPU and letting another thread run is going to be the same as, or greater than, the time it takes to poll and get the data. So we might as well poll. Note also that we don't poll forever; we poll only for PCL711_TIMEOUT number of iterations.
The polling is in place to handle hardware that's not present—if the hardware is missing (or defective), we will time out. Also, notice that we use nanospin() to give up the ISA bus between polling. The nanospin() delay value is selected to be 1 microsecond; this ensures that we poll often enough to minimize the number of times we poll.
In conjunction with the pcl711_read_analog() function, there's a function that sets the gain value. While we could have written the gain value directly into the extended attributes structure's gain member, it's much nicer to have a function to do it, so that we can isolate accesses to that parameter (and change things around if we need to, without changing a bunch of code).
This function is pcl711_set_gain():
void pcl711_set_gain (pcl711_t *pcl, int channel, int gaincode) { if (gaincode < 0 || gaincode > 4) { return; } channel &= 7; pcl -> gains [channel] = gaincode; }
Notice the checking up front to ensure that no bad values are used before we write the value into the gains array member.