Advertisement
merktdom

Untitled

Dec 11th, 2017
57
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.03 KB | None | 0 0
  1. /**
  2. * \mainpage MCP3204/08 value reader program
  3. *
  4. * This program reads values from channels of the MCP3204/08 ADC and prints the
  5. * sum of the channels' values. The channel numbers are taken as command-line
  6. * arguments. * The device via which to access the ADC may be set via the `-d`
  7. * option (`/dev/spidev0.0` by default). The program may be instructed to fetch
  8. * multiple samples using the `-s` option.
  9. *
  10. * According to the datasheet, the MCP3204/08 is accessed via an interface
  11. * resembling SPI. Instructing the ADC to sample a channel and reading the
  12. * value back is done in a simple transaction. In fact, the SPI clock drives the
  13. * spproximation. The MCP3208/04 is an incredible simple, feature-less and
  14. * obsolete 12bit ADC. I have no idea why people would buy this piece of crap.
  15. *
  16. * We are stuck with this chip, because a certain someone ordered it. If you get
  17. * the change to get a new one, with a higher dynamic and an internal gain,
  18. * throw away the MCP.
  19. */
  20.  
  21. // system headers
  22. #include <linux/spi/spidev.h>
  23. #include <linux/types.h>
  24. #include <sys/ioctl.h>
  25. #include <sys/stat.h>
  26.  
  27. #include <fcntl.h>
  28.  
  29. // library headers
  30. #include <errno.h>
  31. #include <stdint.h>
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35. #include <unistd.h>
  36.  
  37.  
  38.  
  39.  
  40. /**
  41. * Prepare the ADC and the SPI interface
  42. *
  43. * This function sets up the SPI interface and the ADC.
  44. *
  45. * @returns 0 on success, -1 on error
  46. */
  47. static int setup_adc(
  48. int fd ///< fd of the ADC
  49. ) {
  50. uint8_t mode = 0;
  51. return ioctl(fd, SPI_IOC_WR_MODE, &mode);
  52. }
  53.  
  54.  
  55. /**
  56. * Read a channel's value from the ADC
  57. *
  58. * Read the value of a specific channel from the ADC. Multiple samples may be
  59. * read. In this case, the function will return the average of all samples. This
  60. * lets the caller choose the number of samples freely without adjusting any
  61. * calculation carried out afterwards.
  62. *
  63. * Obviously, the ADC has to be set up before calling this function.
  64. *
  65. * @returns the channel's value, or -1 if an error occured
  66. */
  67. static int read_channel(
  68. int fd, ///< fd of the ADC
  69. unsigned int channel, ///< channel to read
  70. unsigned int samples ///< number of samples
  71. ) {
  72. // A transaction looks like the following:
  73. //
  74. // TX 1|M|C2-C0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|
  75. // RX 0|0|0|0|0|0| V11-V0 | V1-V11 |0|0|
  76. //
  77. // The transaction is started by writing a single `1`, followed by the mode
  78. // (`1` for single mode, e.g. not differential) and the channel number (MBS
  79. // first). The response is delivered a single clock later. It consist of the
  80. // freshly sampled value, MSB first, followed by the same value in reversed
  81. // bit order. Note that the LSB is shared.
  82.  
  83. // We prepare a single TX buffer instructing the ADC to sample the channel.
  84. // The buffer is reused in all transactions.
  85. uint8_t tx[4] = {0xc0 | channel << 3, 0};
  86.  
  87. // We fetch multiple samples in one batch. Therefore, we need multiple RX
  88. // buffers. Each single buffer is a sequence of 4 bytes.
  89. uint8_t rx[4 * samples];
  90.  
  91. // We prepare a transaction for each sample
  92. struct spi_ioc_transfer tr[samples];
  93. memset(tr, 0, sizeof(tr[0]) * samples);
  94. for (unsigned int i = 0; i < samples; ++i) {
  95. tr[i].tx_buf = (__u64) tx;
  96. tr[i].rx_buf = (__u64) &rx[i*4];
  97. tr[i].len = 4;
  98. tr[i].delay_usecs = 1;
  99. // we need >10kHz in order to prevent the S&H from floating away
  100. tr[i].speed_hz = 20000;
  101. tr[i].bits_per_word = 8;
  102. tr[i].cs_change = 1;
  103. }
  104.  
  105. // Perform the sampling
  106. if (ioctl(fd, SPI_IOC_MESSAGE(samples), tr) < 0)
  107. return -1;
  108.  
  109. // Calculate the AVG as advertised
  110. unsigned int sum = 0;
  111. for (unsigned int i = 0; i < samples; ++i) {
  112. // The RX buffer is filled byte-wise. For bit-fiddling we need to
  113. // convert it to a known endianess. We chose to interpret the buffer as
  114. // low-endian. This way, we can extract the value by simply shifting
  115. // it to the right.
  116. uint32_t buf = ((uint32_t) rx[4*i+0]) << 24 |
  117. ((uint32_t) rx[4*i+1]) << 16 |
  118. ((uint32_t) rx[4*i+2]) << 8 |
  119. ((uint32_t) rx[4*i+3]);
  120. sum += buf >> 13;
  121. }
  122. return sum / samples;
  123. }
  124.  
  125.  
  126.  
  127.  
  128. /**
  129. * Main routine, obviously
  130. *
  131. * Go figure
  132. */
  133. int main(int argc, char* argv[]) {
  134. // Defaults
  135. const char* device = "/dev/spidev0.0";
  136. unsigned int samples = 1;
  137.  
  138. // Read options
  139. int opt;
  140. while ((opt = getopt(argc, argv, "d:n:")) != -1) {
  141. switch (opt) {
  142. case 'd':
  143. device = optarg;
  144. break;
  145. case 'n':
  146. samples = atoi(optarg);
  147. break;
  148. default:
  149. fprintf(stderr, "Usage: %s, [-d device] [-n sample_count] channels...\n", argv[0]);
  150. exit(EXIT_FAILURE);
  151. }
  152. }
  153.  
  154.  
  155. // Prepare the device
  156. int fd = open(device, O_RDWR);
  157. if (fd < 0) {
  158. fprintf(stderr, "Could not open device: %s\n", strerror(errno));
  159. exit(EXIT_FAILURE);
  160. }
  161.  
  162. if (setup_adc(fd) < 0) {
  163. fprintf(stderr, "Could not setup ADC: %s\n", strerror(errno));
  164. exit(EXIT_FAILURE);
  165. }
  166.  
  167.  
  168. // Read the values and calculate the sum
  169. unsigned int sum = 0;
  170. unsigned int count = 0;
  171. while (optind < argc) {
  172. unsigned int channel = atoi(argv[optind]);
  173. // The MCP3208 only has 8 channels.
  174. if (channel > 8) {
  175. fprintf(stderr, "Invalid channel: %s\n", argv[optind]);
  176. exit(EXIT_FAILURE);
  177. }
  178.  
  179. int value = read_channel(fd, channel, samples);
  180. if (value < 0) {
  181. fprintf(stderr, "Error reading channel %d: %s\n", channel, strerror(errno));
  182. exit(EXIT_FAILURE);
  183. }
  184.  
  185. sum += value;
  186. ++optind;
  187. ++count;
  188. }
  189.  
  190. // Success! Print the result.
  191. printf("%d\n", sum);
  192. exit(EXIT_SUCCESS);
  193. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement