The Pico In MicroPython: ADC
Written by Mike James & Harry Fairhead   
Monday, 13 March 2023
Article Index
The Pico In MicroPython: ADC
The MCP3008 SPI ADC
The Protocol

Basic Configuration

Now we come to the configuration of the SPI bus. We have some rough figures for the SPI clock speed - around 10kHz to a little more than 1.35MHz. So a clock frequency of 500kHz seems a reasonable starting point.

From the datasheet, the chip select has to be active low and, by default, data is sent most significant bit first for both the master and the slave. The only puzzle is what mode to use? This is listed in the datasheet as mode 0 0 with clock active high or mode 1 1 with clock active low. For simplicity we will use mode 0 0.

We now have enough information to initialize the slave:

spi = SPI(0, sck=Pin(18), miso=Pin(16), mosi=Pin(19))
spi.init(baudrate=500_000, bits=8, polarity=0, 
phase=0, firstbit=SPI.MSB) CS = Pin(17, Pin.OUT) CS.high() sleep_ms(1)

The Protocol

Now we have the SPI initialized and ready to transfer data, but what data do we transfer? As already discussed in the previous chapter, the SPI bus doesn't have any standard commands or addressing structure. Each device responds to data sent in different ways and sends data back in different ways. You simply have to read the datasheet to find out what the commands and responses are.

Reading the datasheet might be initially confusing because it says that you have to send five bits to the slave - a start bit, a bit that selects its operating mode single or differential, and a 3-bit channel number. The operating mode is 1 for single-ended and 0 for differential.

So to read Channel 3, i.e. 011, in single-ended mode you would send the slave:

	11011xxx

where an x can take either value. In response, the slave holds its output in a high impedance state until the sixth clock pulse, then sends a zero bit on the seventh, followed by bit 9 of the data on the eighth clock pulse.

That is, the slave sends back:

	xxxxxx0b9

where x means indeterminate.

The remaining nine bits are sent back in response to the next nine clock pulses. This means you have to transfer three bytes to get all ten bits of data. This all makes reading the data in 8-bit chunks confusing.

The datasheet suggests a different way of doing the job that delivers the data more neatly packed into three bytes. What it suggests to send a single byte is:

	00000001

At the same time, the slave transfers random data, which is ignored. The final 1 is treated as the start bit. If you now transfer a second byte with most significant bit indicating single or differential mode, then a 3-bit channel address and the remaining bits set to 0, the slave will respond with the null and the top two bits of the conversion. Now all you have to do to get the final eight bits of data is to read a third byte:

adc6
This way you get two neat bytes containing the data with all the low-order bits in their correct positions.

Using this information we can now write some instructions that read a given channel. For example, to read Channel 0 we first send a byte set to 0x01 as the start bit and ignore the byte the slave transfers. Next we send 0x80 to select single-ended and Channel 0 and keep the byte the slave sends back as the two high-order bits. Finally, we send a zero byte (0x00) so that we get the low-order bits from the slave:

CS.low()
write=bytearray([0x01, 0x80, 0x00])
read=bytearray(3)
spi.write_readinto(write,read)
CS.high()

Notice you cannot send the three bytes one at a time using transfer because that results in the CS line being deactivated between the transfer of each byte.

To get the data out of rBuff we need to do some bit manipulation:

data =  (read[1] & 0x03) << 8 |  read[2]    

The first part of the expression extracts the low three bits from the first byte the slave sent and, as these are the most significant bits, they are shifted up eight places. The rest of the bits are then ORed with them to give the full 10‑bit result. To convert to volts we use:

volts =  data * 3.3 / 1023.0

assuming that VREF is 3.3V.

In a real application you would also need to convert the voltage to some other quantity, like temperature or light level.

If you connect a logic analyzer to the SPI bus you will see something like:

adc6
You can see the commands and the response, in this case a reading of 0.693V.

The complete program is:

from utime import sleep_ms
from machine import Pin, SPI
from time import sleep
spi = SPI(0, sck=Pin(18), miso=Pin(16), mosi=Pin(19))
spi.init(baudrate=500_000, bits=8, polarity=0, 
phase=0, firstbit=SPI.MSB) CS = Pin(17, Pin.OUT) CS.high() sleep_ms(1) CS.low() write=bytearray([0x01, 0x80, 0x00]) read=bytearray(3) spi.write_readinto(write,read) CS.high() data = (read[1] & 0x03) << 8 | read[2] volts = data * 3.3 / 1023.0 print(volts) spi.deinit()

In chapter but not in this extract

  • SPI ADC Class
  • How Fast?

Summary

  • The Pico has a single ADC with four inputs. It is subject to a lot of noise from the power supply and circuits around it which reduces its accuracy.

  • You can read individual input lines using the ADC class.

  • The ADC class doesn’t support many of the more advanced features of the Pico’s ADC hardware.

  • Making SPI work with any particular device has four steps:

    1. Discover how to connect the device to the SPI pins. This is a matter of identifying pinouts and mostly what chip selects are supported.

    2. Find out how to configure the Pi's SPI bus to work with the device. This is mostly a matter of clock speed and mode.

    3. Identify the commands that you need to send to the device to get it to do something and what data it sends back as a response.

    4. Find, or work out, the relationship between the raw reading, the voltage and the quantity the voltage represents.

  • The MCP3000 range of A-to-D converters is very easy to use via SPI.

  • You can read data at rates as fast as 6kHz.

 adc3

Programming the Raspberry Pi Pico/W In MicroPython Second Edition

By Harry Fairhead & Mike James

picopython2e360

Buy from Amazon.

Contents

  • Preface
  • Chapter 1 The Raspberry Pi Pico – Before We Begin
  • Chapter 2 Getting Started
  • Chapter 3 Getting Started With The GPIO
  • Chapter 4 Simple Output
  • Chapter 5 Some Electronics
  • Chapter 6 Simple Input
             Extract: Simple Input 
  • Chapter 7 Advanced Input – Events and Interrupts
  • Chapter 8 Pulse Width Modulation
             Extract: PWM 
  • Chapter 9 Controlling Motors And Servos
             Extract: DC Motors
  • Chapter 10 Getting Started With The SPI Bus
  • Chapter 11 A-To-D and The SPI Bus ***NEW!
  • Chapter 12 Using The I2C Bus
  • Chapter 13 Using The PIO   
  • Chapter 14 The DHT22 Sensor Implementing A Custom Protocol
             Extract: A PIO Driver For The DHT22  
  • Chapter 15 The 1‑Wire Bus And The DS1820
  • Chapter 16 The Serial Port
  • Chapter 17 Using The Pico W - WiFi
             Extract: HTTP Client 
  • Chapter 18 Asyncio And Servers
  • Chapter 19 Direct To The Hardware
             Extract: Direct To The Hardware

Also of interest:

Raspberry Pico File System

<ASIN:1871962803>

<ASIN:B0BR8LWYMZ>

<ASIN:187196279X>

<ASIN:B0BL1HS3QD>

 

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

Banner


Running PostgreSQL Inside Your Browser With PGLite
18/03/2024

Thanks to WebAssembly we can now enjoy PostgreSQL inside the browser so that we can build reactive, realtime, local-first apps directly on Postgres. PGLite is about to make this even easier.



SnapCode: A Java IDE for the Web
27/02/2024

Thanks to CheerpJ and WebAssembly you can now run a Java IDE inside your browser and local first.This is SnapCode, and while lightweight and in-browser, is to be not underestimated.


More News

raspberry pi books

 

Comments




or email your comment to: comments@i-programmer.info



Last Updated ( Wednesday, 15 March 2023 )