Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

fast MCP3008 reader for Raspberry Pi

License

NotificationsYou must be signed in to change notification settings

nagimov/mcp3008hwspi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

This is a simple command line tool for Raspberry Pi interfacing with Microchip's MCP3008 ADC (datasheet) over SPI interface. It achieves sampling rates exceeding 100kHz with a slight modification of a stock raspberry kernel.

Even without kernel modification, higher sampling rates (up to ~63 kHz on Raspberry Pi 3B+) can be achieved when compared to using Raspberry Pi'sMCP3008 kernel driver orbit-banging via pigpio.

The approach is based onthis post andthis implementation describing how to reduce SPI transaction roundtrip per sample.

Usage

mcp3008hwspi (version 0.0.1)Reads data from MCP3008 ADC through hardware SPI interface on Raspberry Pi.Online help, docs & bug reports: <https://github.com/nagimov/mcp3008hwspi>Usage: mcp3008hwspi [OPTION]...Mandatory arguments to long options are mandatory for short options too.  -b, --block B        read B blocks per every scan of all specified channels,                       1 <= BPR <= 511 (default: 1) [integer];                       multiple channels are always read as a single block;  -r, --clockrate CR   SPI clock rate, Hz, 1000000 <= CR <= 3600000                       (default: 3600000) [integer];                       MCP3008 must be powered from 5V for 3.6MHz clock rate;  -c, --channels CH    read specified channels CH, 0 <= CH <= 7 (default: 0);                       multiple channels can be specified, e.g. -c 0123;                       all channels are read as a single block, e.g. if ran as                       <mcp3008hwspi -c 0123 -b 2>                       8 blocks are transmitted per SPI read (4 channels x 2);  -s, --save FILE      save data to specified FILE (if not specified, data is                       printed to stdout);  -n, --samples N      set the number of samples per channel to be read to N                       (default: 1000 samples) [integer];  -f, --freq FREQ      set the sampling rate to FREQ, samples per second                       (default: 0 Hz) [integer];                       if set to 0, ADC is sampled at maximum achievable rate,                       if set to > 0, --block is reset to 1;Data is streamed in comma separated format, e. g.:  sample ch0,  value ch0,  sample ch1,  value ch1           0,       1023,           1,        512           2,       1022,           3,        513         ...,        ...,         ...,        ...  samples are (hopefully) equally spaced in time;  channels are read sequentially with equal time delays between samples;  value chX shows raw 10-bit integer readback from channel X;  average sampling rate is written to both stdout and output file header.Exit status:  0  if OK  1  if error occurred while reading or wrong cmdline arguments.Example:  mcp3008hwspi  -r 3600000  -c 0123  -s out.csv  -f 0  -n 1000  -b 25                      ^         ^         ^        ^      ^        ^                      |         |         |        |      |        |  3.6 MHz SPI clock --+         |         |        |      |        |  read channels 0, 1, 2 and 3 --+         |        |      |        |  save data to output file 'out.csv' -----+        |      |        |  set sampling frequency to max achievable rate ---+      |        |  read 1000 samples per channel (1000 x 4 = 4000 total) --+        |  read channels in blocks of 25 (25 x 4 = 100 blocks per SPI read)-+

Notes:

  • 5V supply is required for 3.6 MHz clock rate (max clock rate at 3.3V supply is ~1.0 MHz);
  • sampling rate is integrated over all channels (e.g. 100 kHz for one channel ~= 50 kHz for two channels);
  • when multiple channels are specified, all channels are always read as a single block, e.g.-c 01 -b 20 means both channels 0 and 1 will be read in blocks of 20 samples each, 40 blocks per SPI transaction in total;
  • try experimenting with the block size if sampling rate is important to you (seeBlock size and sampling rate).

Block size and sampling rate

Parameter-b defines number of reads batched to a single SPI transaction. Whenmcp3008hwspi is used with a patched kernel (seeSPI kernel driver patch), optimal value of-b is somewhere around100. Note that when more than a single channel is specified, actual number of samples received per transaction is multiplied by a number of channels, since all the channels are always read as a single block. E.g. in order to read the first five channels (e.g.-c 01234), optimal value of the block size is expected to be around20.

When unmodified raspberry kernel is used, optimal value of parameter-b is1. Since it is a default value, in this case it can be safely omitted.

Compilation

No external dependencies or configurations required:

git clone https://github.com/nagimov/mcp3008hwspicd mcp3008hwspimakesudo make install

Build is only tested on Raspbian OS.

Wiring and testing

Any audio DAC can be used as a simple signal generator for testing a kHz-range sampling rate ADC. There are plenty of PC/smartphone applications for this purpose (for the following examplesFunction Generator app is used).

Simple fritzing diagram for testing purposes:

MCP3008

  • MCP3008 is powered (VDD) from 5V but referenced (VREF) from 3.3V in order to increase bit-per-volt resolution for a small amplitude audio signal;
  • 2 x 100 kOhm resistors create a voltage divider in order to bias audio input by 1.65V;
  • 220 Ohm resistor limits inrush currents to capacitors;
  • fairly high bypass capacitance is recommended on both VDD and VREF lines (datasheet recommends 1 μF, however readings are more stable at 2.2 μF, possibly due to Raspberry Pi's voltage regulation);
  • right and left audio channels are connected to channels 0 and 1 of MCP3008, bias voltage is connected to channel 2.

I use python with numpy and matplotlib to easily read and visualize data. Save this script asplot.py.

importsysimportnumpyasnpimportmatplotlib.pyplotaspltplt.figure(figsize=(9,2))data=np.genfromtxt(sys.argv[1],skip_header=1,delimiter=',',names=True)cols=iter(data.dtype.names)fors,vinzip(cols,cols):plt.plot(data[s],data[v],'.-',label='ch %s'% [int(d)fordinsifd.isdigit()][0],linewidth=0.5,markersize=1)plt.tight_layout()plt.legend(loc='upper right')plt.savefig('plot.png',dpi=100)# or plt.show()

setup

Destructive interference

For the first test,Function Generator is set to output two 1 kHz sine wave signals from the right and left channels, with a phase difference of 180 degrees. Due to destructive interference of sound waves, there will be no sound coming out of the smartphone speaker with the audio cable unplugged. However signals are still there:

  • read three channels (right, left and bias), 70 samples each:
$ mcp3008hwspi -c 012 -f 0 -n 70 -s out-1kHz.csv -b 100.00 seconds, 210 samples, 103092.78 Hz total sample rate, 34364.26 Hz per-channel sample rateWriting to the output file...
  • at ~35 kHz per channel, there should be approximately70samples/(35kHz/1kHz)=2 full periods of each sine wave:
python3 plot.py out-1kHz.csv

plot-1kHz

Frequency sweep

For the second test,Function Generator is set to output a frequency sweep from 20 Hz to 20 kHz, mode - bounce, time - 0.01s. Due to a limited frequency bandwidth of simple audio DAC, signal roll-off can be expected at higher frequencies:

  • read 2000 samples from channel 0:
$ mcp3008hwspi -c 0 -f 0 -n 2000 -s out-sweep.csv -b 1000.02 seconds, 2000 samples, 104613.45 Hz total sample rate, 104613.45 Hz per-channel sample rateWriting to the output file...
  • at ~100 kHz per channel, there should be approximately2000/100kHz/0.01s=2 frequency transitions (from high to low and from low to high):
python3 plot.py out-sweep.csv

plot-sweep

Benchmarks

Achievable sampling rate depends on multiple factors (Raspberry Pi model, OS version, CPU load during the benchmark, etc.). For the following test cases,mcp3008hwspi is ran three times and the second best result is recorded. When using a modified kernel, a block size of 100 is used for a single channel and a block size of 12 is used for eight channels (100/8=~12).

If you have more benchmarking data, please share via PR or submit an issue.

  • single channel, Raspbian Buster Lite (2020-02-12), stock kernel, Raspberry Pi 3B+:
$ mcp3008hwspi -c 0 -f 0 -n 1000000 -s out.csv15.74 seconds, 1000000 samples, 63522.65 Hz total sample rate, 63522.65 Hz per-channel sample rateWriting to the output file...
  • eight channels, Raspbian Buster Lite (2020-02-12), stock kernel, Raspberry Pi 3B+:
$ mcp3008hwspi -c 01234567 -f 0 -n 100000 -s out.csv15.39 seconds, 800000 samples, 51985.33 Hz total sample rate, 6498.17 Hz per-channel sample rateWriting to the output file...
  • single channel, Raspbian Buster Lite (2020-02-12), modified kernel, Raspberry Pi 3B+:
$ mcp3008hwspi -c 0 -f 0 -n 1000000 -s out.csv -b 1009.57 seconds, 1000000 samples, 104467.59 Hz total sample rate, 104467.59 Hz per-channel sample rateWriting to the output file...
  • eight channels, Raspbian Buster Lite (2020-02-12), modified kernel, Raspberry Pi 3B+:
$ mcp3008hwspi -c 01234567 -f 0 -n 100000 -s out.csv -b 127.66 seconds, 800064 samples, 104500.97 Hz total sample rate, 13062.62 Hz per-channel sample rateWriting to the output file...

SPI kernel driver patch

You only need to follow this part if higher sampling rates are required compared to a stock raspberry kernel. Don't be discouraged by a scary "kernel thing" - the process is straightforward and well documented.

Note: the following applies to kernel version 4.19. Checkthis page for the latest kernel building instructions.

What needs to be changed

MCP3008's sampling rate is limited to 200 kHz, with its SPI clock rate defined as18*f_SAMPLE, transferring 18 bits per sample at a maximum clock rate18*200kHz=3.6MHz (see more info in thedatasheet). However SPI driver of raspberry kernel communicates using 8-bit words (can be seenhere) and wastes a clock cycle per every byte transmitted (can be seenhere), so maximum theoretical sampling rate is down to3.6MHz/(3*(8+1))=133.3kHz. Another large inefficiency comes from a 10 us delay introduced after every single toggle of CS line defined in a corespi.c driver (can be seenhere). Fortunately, this delay can be removed entirely without affecting SPI functionality when running at 3.6 MHz clock rate. This change alone increases sampling rate up to >100 kHz.

Note: this modification will very likely affect SPI communication with other devices, especially at higher clock rates. A full explanation including math and timing calculations is given inthis post.

Step by step instructions

It takes a couple of hours to compile 4.19 kernel on a Raspberry Pi 3B+. You can use a linux workstation or VM to speed things up - manuals for cross-compiling can be foundhere. If you are using Raspberry Pi 2 or older, cross-compiling is probably a better option.

For simplicity and portability reasons, the following instructions are only provided for building the kernel locally on a Raspberry Pi itself. Make sure to provide sufficient CPU cooling, especially for Pi 4 models - full CPU load is known to cause overheating.

  • install required packages:
sudo apt-get updatesudo apt-get install git bc bison flex libssl-dev make
  • clone raspberry kernel:
    • make sure to include--depth=1 to prevent git from copying the entire history
    • make sure to change--branch rpi-4.19.y to the current version of your raspberry kernel (rununame -r to display the kernel version)
    • git will fetch around 200 MB and unpack it, depending on your connectivity this might take 10-20 minutes
git clone --depth=1 --branch rpi-4.19.y https://github.com/raspberrypi/linux
  • modifyspi.c driver (get rid of the above mentioned 10 us delay) and make sure that the line is commented:
sed -i "s/udelay(10);/\/\/udelay(10);/" linux/drivers/spi/spi.ccat linux/drivers/spi/spi.c | grep "udelay(10);"

Kernel configuration commands are dependent on the model of Raspberry Pi. This is a relatively quick step (takes under a minute):

  • for Pi 1, Pi Zero, Pi Zero W, or Compute Module:
cd linuxKERNEL=kernelmake bcmrpi_defconfig
  • for Pi 2, Pi 3, Pi 3+ or Compute Module 3:
cd linuxKERNEL=kernel7make bcm2709_defconfig
  • for Pi 4:
cd linuxKERNEL=kernel7lmake bcm2711_defconfig

Next step is building kernel modules. Grab a book - this will take a couple of hours on Raspberry Pi 3B+:

make -j4 zImage modules dtbs

Install compiled modules (another ~3 minutes):

sudo make modules_install

Final step is to copy everything onto a boot partition:

sudo cp arch/arm/boot/dts/*.dtb /boot/sudo cp arch/arm/boot/dts/overlays/*.dtb* /boot/overlays/sudo cp arch/arm/boot/dts/overlays/README /boot/overlays/sudo cp arch/arm/boot/zImage /boot/$KERNEL.img

Reboot the system to a newly built kernel.


[8]ページ先頭

©2009-2025 Movatter.jp