/ adafruit_boardtest / boardtest_spi.py
boardtest_spi.py
1 # The MIT License (MIT) 2 # 3 # Copyright (c) 2018 Shawn Hymel for Adafruit Industries 4 # 5 # Permission is hereby granted, free of charge, to any person obtaining a copy 6 # of this software and associated documentation files (the "Software"), to deal 7 # in the Software without restriction, including without limitation the rights 8 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 # copies of the Software, and to permit persons to whom the Software is 10 # furnished to do so, subject to the following conditions: 11 # 12 # The above copyright notice and this permission notice shall be included in 13 # all copies or substantial portions of the Software. 14 # 15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 # THE SOFTWARE. 22 """ 23 `adafruit_boardtest.boardtest_spi` 24 ==================================================== 25 Performs random writes and reads to SPI EEPROM. 26 27 Run this script as its own main.py to individually run the test, or compile 28 with mpy-cross and call from separate test script. 29 30 * Author(s): Shawn Hymel for Adafruit Industries 31 32 Implementation Notes 33 -------------------- 34 35 **Hardware:** 36 37 * `Microchip 25AA040A SPI EEPROM <https://www.digikey.com/product-detail/en/\ 38 microchip-technology/25AA040A-I-P/25AA040A-I-P-ND/1212469>`_ 39 40 **Software and Dependencies:** 41 42 * Adafruit CircuitPython firmware for the supported boards: 43 https://github.com/adafruit/circuitpython/releases 44 * Adafruit's Bus Device library: 45 https://github.com/adafruit/Adafruit_CircuitPython_BusDevice 46 47 """ 48 49 import random 50 import time 51 52 import board 53 import digitalio 54 import busio 55 56 __version__ = "0.0.0-auto.0" 57 __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BoardTest.git" 58 59 # Constants 60 MOSI_PIN_NAME = "MOSI" 61 MISO_PIN_NAME = "MISO" 62 SCK_PIN_NAME = "SCK" 63 CS_PIN_NAME = "D2" 64 BAUD_RATE = 100000 # Bits per second 65 NUM_SPI_TESTS = 10 # Number of times to write and read EEPROM values 66 67 # Microchip 25AA040A EEPROM SPI commands and bits 68 EEPROM_SPI_WRSR = 0x01 69 EEPROM_SPI_WRITE = 0x02 70 EEPROM_SPI_READ = 0x03 71 EEPROM_SPI_WRDI = 0x04 72 EEPROM_SPI_RDSR = 0x05 73 EEPROM_SPI_WREN = 0x06 74 EEPROM_SPI_WIP_BIT = 0 75 EEPROM_SPI_MAX_ADDR = 255 # Self-imposed max memory address 76 EEPROM_I2C_MAX_ADDR = 255 # Self-imposed max memory address 77 78 # Test result strings 79 PASS = "PASS" 80 FAIL = "FAIL" 81 NA = "N/A" 82 83 # Wait for WIP bit to go low 84 def _eeprom_spi_wait(spi, csel, timeout=1.0): 85 86 # Continually read from STATUS register 87 timestamp = time.monotonic() 88 while time.monotonic() < timestamp + timeout: 89 90 # Perfrom RDSR operation 91 csel.value = False 92 result = bytearray(1) 93 spi.write(bytearray([EEPROM_SPI_RDSR])) 94 spi.readinto(result) 95 csel.value = True 96 97 # Mask out and compare WIP bit 98 if (result[0] & (1 << EEPROM_SPI_WIP_BIT)) == 0: 99 return True 100 101 return False 102 103 104 # Write to address. Returns status (True for successful write, False otherwise) 105 def _eeprom_spi_write_byte(spi, csel, address, data, timeout=1.0): 106 107 # Make sure address is only one byte: 108 if address > 255: 109 return False 110 111 # Make sure data is only one byte: 112 if data > 255: 113 return False 114 115 # Wait for WIP to be low 116 if not _eeprom_spi_wait(spi, csel, timeout): 117 return False 118 119 # Enable writing 120 csel.value = False 121 spi.write(bytearray([EEPROM_SPI_WREN])) 122 csel.value = True 123 124 # Write to address 125 csel.value = False 126 spi.write(bytearray([EEPROM_SPI_WRITE, address, data])) 127 csel.value = True 128 129 return True 130 131 132 # Read from address. Returns tuple [status, result] 133 def _eeprom_spi_read_byte(spi, csel, address, timeout=1.0): 134 135 # Make sure address is only one byte: 136 if address > 255: 137 return False, bytearray() 138 139 # Wait for WIP to be low 140 if not _eeprom_spi_wait(spi, csel, timeout): 141 return False, bytearray() 142 143 # Read byte from address 144 csel.value = False 145 result = bytearray(1) 146 spi.write(bytearray([EEPROM_SPI_READ, address])) 147 spi.readinto(result) 148 csel.value = True 149 150 return True, result 151 152 153 def run_test( 154 pins, 155 mosi_pin=MOSI_PIN_NAME, 156 miso_pin=MISO_PIN_NAME, 157 sck_pin=SCK_PIN_NAME, 158 cs_pin=CS_PIN_NAME, 159 ): 160 161 """ 162 Performs random writes and reads to file on attached SD card. 163 164 :param list[str] pins: list of pins to run the test on 165 :param str mosi_pin: pin name of SPI MOSI 166 :param str miso_pin: pin name of SPI MISO 167 :param str sck_pin: pin name of SPI SCK 168 :param str cs_pin: pin name of SPI CS 169 :return: tuple(str, list[str]): test result followed by list of pins tested 170 """ 171 172 # Write values to SPI EEPROM and verify the values match 173 if list(set(pins).intersection(set([mosi_pin, miso_pin, sck_pin]))): 174 175 # Tell user to connect EEPROM chip 176 print("Connect a Microchip 25AA040A EEPROM SPI chip.") 177 print("Connect " + cs_pin + " to the CS pin on the 25AA040.") 178 print("Press enter to continue.") 179 input() 180 181 # Configure CS pin 182 csel = digitalio.DigitalInOut(getattr(board, cs_pin)) 183 csel.direction = digitalio.Direction.OUTPUT 184 csel.value = True 185 186 # Set up SPI 187 spi = busio.SPI( 188 getattr(board, sck_pin), 189 MOSI=getattr(board, mosi_pin), 190 MISO=getattr(board, miso_pin), 191 ) 192 193 # Wait for SPI lock 194 while not spi.try_lock(): 195 pass 196 spi.configure(baudrate=BAUD_RATE, phase=0, polarity=0) 197 198 # Pick a random address, write to it, read from it, and see if they match 199 pass_test = True 200 for _ in range(NUM_SPI_TESTS): 201 202 # Randomly pick an address and a data value (one byte) 203 mem_addr = random.randint(0, EEPROM_SPI_MAX_ADDR) 204 mem_data = random.randint(0, 255) 205 print("Address:\t" + hex(mem_addr)) 206 print("Writing:\t" + hex(mem_data)) 207 208 # Try writing this random value to the random address 209 result = _eeprom_spi_write_byte(spi, csel, mem_addr, mem_data) 210 if not result: 211 print("FAIL: SPI could not communicate") 212 pass_test = False 213 break 214 215 # Try reading the written value back from EEPRom 216 result = _eeprom_spi_read_byte(spi, csel, mem_addr) 217 print("Read:\t\t" + hex(result[1][0])) 218 print() 219 if not result[0]: 220 print("FAIL: SPI could not communicate") 221 pass_test = False 222 break 223 224 # Compare the read value to the original value 225 if result[1][0] != mem_data: 226 print("FAIL: Data does not match") 227 pass_test = False 228 break 229 230 # Release SPI pins 231 spi.deinit() 232 233 # Return results 234 if pass_test: 235 return PASS, [mosi_pin, miso_pin, sck_pin] 236 237 return FAIL, [mosi_pin, miso_pin, sck_pin] 238 239 # Else (no pins found) 240 print("No SPI pins found") 241 return NA, [] 242 243 244 def _main(): 245 246 # List out all the pins available to us 247 pins = list(dir(board)) 248 print() 249 print("All pins found:", end=" ") 250 251 # Print pins 252 for pin in pins: 253 print(pin, end=" ") 254 print("\n") 255 256 # Run test 257 result = run_test(pins) 258 print() 259 print(result[0]) 260 print("Pins tested: " + str(result[1])) 261 262 263 # Execute only if run as main.py or code.py 264 if __name__ == "__main__": 265 _main()