Add initial files for the HH Hacking 101 - code and infos about board will follow soon
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
# hw-hacking-101
|
||||
# Hardware-Hacking-101
|
||||
|
||||
This repo contains the tools, infos, hardware details for the hardware hacking 101 sessions.
|
||||
Hardware Hacking Learning Platform for Educational Usage like Schools, University etc.
|
||||
|
||||
9
code-snippets/ADC_Photo_Resistor.py
Normal file
9
code-snippets/ADC_Photo_Resistor.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from machine import ADC, Pin
|
||||
from time import sleep
|
||||
|
||||
adc = ADC(Pin(34)) # create an ADC object acting on a pin
|
||||
adc.atten(ADC.ATTN_11DB) # 3.3V max
|
||||
|
||||
while True:
|
||||
print("Analog Value: " , adc.read())
|
||||
sleep(1)
|
||||
55
code-snippets/Button_LED_Test.py
Normal file
55
code-snippets/Button_LED_Test.py
Normal file
@@ -0,0 +1,55 @@
|
||||
## Simple Button and LED test
|
||||
# yellow led will blink
|
||||
# red + green will be off button is pressed
|
||||
|
||||
from machine import Pin
|
||||
import time
|
||||
|
||||
## define led pins
|
||||
led_gr_p = 21
|
||||
led_re_p = 22
|
||||
led_ye_p = 17
|
||||
|
||||
## define switch pins
|
||||
sw1_p = 32
|
||||
sw2_p = 33
|
||||
|
||||
|
||||
## init leds
|
||||
led_gr = Pin(led_gr_p, Pin.OUT)
|
||||
led_gr.off() # switch on led - inverted logic!
|
||||
|
||||
led_re = Pin(led_re_p, Pin.OUT)
|
||||
led_re.off() # switch on led - inverted logic!
|
||||
|
||||
led_ye = Pin(led_ye_p, Pin.OUT)
|
||||
led_ye.off() # switch on led - inverted logic!
|
||||
|
||||
|
||||
## int buttons (pull-up)
|
||||
sw1 = Pin(sw1_p, Pin.IN)
|
||||
sw2 = Pin(sw2_p, Pin.IN)
|
||||
|
||||
while True:
|
||||
if sw1.value():
|
||||
print("sw1 true")
|
||||
led_re.on()
|
||||
#led_ye.on()
|
||||
else:
|
||||
print("sw1 false")
|
||||
led_re.off()
|
||||
#led_ye.off()
|
||||
if sw2.value():
|
||||
print("sw2 true")
|
||||
led_gr.on()
|
||||
else:
|
||||
print("sw2 false")
|
||||
led_gr.off()
|
||||
|
||||
# toggle led
|
||||
if led_ye.value():
|
||||
led_ye.off();
|
||||
else:
|
||||
led_ye.on();
|
||||
time.sleep(0.5)
|
||||
|
||||
83
code-snippets/KeyPad_4x4_esp32.py
Normal file
83
code-snippets/KeyPad_4x4_esp32.py
Normal file
@@ -0,0 +1,83 @@
|
||||
# KeyPad Vers #2
|
||||
# Tony Goodhew 9th May 2020
|
||||
# https://www.instructables.com/Using-a-4x4-KeyPad-With-CircuitPython/
|
||||
#
|
||||
# Changes by Christopher Scheuring 2022-05-13 for ESP32 support
|
||||
#
|
||||
# Connections left to right on KeyPad
|
||||
# 0 1 2 3 4 5 6 7
|
||||
# 13 12 14 27 15 02 04 16
|
||||
|
||||
from machine import Pin
|
||||
import gc
|
||||
import time
|
||||
|
||||
gc.collect() # make some room
|
||||
|
||||
# Set up LED on pin 17
|
||||
led_pin = 17
|
||||
|
||||
led = Pin(led_pin, Pin.OUT)
|
||||
led.on() # switch off led - inverted logic!
|
||||
|
||||
# Set up Rows
|
||||
rows = []
|
||||
for p in [13, 12, 14, 27]:
|
||||
row = Pin(p, Pin.OUT)
|
||||
rows.append(row)
|
||||
# anodes OFF
|
||||
for i in range(4):
|
||||
rows[i].off()
|
||||
|
||||
# Set up columns
|
||||
cols = []
|
||||
for p in [15, 02, 04, 16]:
|
||||
col = Pin(p, Pin.IN, Pin.PULL_DOWN)
|
||||
cols.append(col)
|
||||
|
||||
def getkey(): # Returns -999 or key value
|
||||
values = [1,2,3,10, 4,5,6,11, 7,8,9,12, 14,0,15,13]
|
||||
val = -999 # Error value for no key press
|
||||
for count in range(10): # Try to get key press 10 times
|
||||
for r in range(4): # Rows, one at a time
|
||||
rows[r].on() # row HIGH
|
||||
for c in range(4): # Test columns, one at a time
|
||||
if cols[c].value() == 1: # Is column HIGH?
|
||||
p = r * 4 + c # Pointer to values list
|
||||
val = values[p]
|
||||
count = 11 # This stops looping
|
||||
led.off() # Flash LED ON if key pressed
|
||||
rows[r].off() # row LOW
|
||||
time.sleep(0.2) # Debounce
|
||||
led.on() # LED OFF
|
||||
return val
|
||||
|
||||
def getvalue(digits): # Number of digits
|
||||
result = 0
|
||||
count = 0
|
||||
while True:
|
||||
x = getkey()
|
||||
if x != -999 and x < 10: # Check if numeric key pressed
|
||||
result = result * 10 + x
|
||||
print(result)
|
||||
count = count + 1
|
||||
if count == digits:
|
||||
return result
|
||||
|
||||
# +++++ Main +++++
|
||||
print("Press 4 numeric (BLUE) keys")
|
||||
y = getvalue(4)
|
||||
print('\n',y)
|
||||
|
||||
print("\nPress any of the keys\n # halts the program")
|
||||
|
||||
characters =["1","2","3","4","5","6","7","8","9","A","B","C","D","*","#","0"]
|
||||
running = True
|
||||
while running:
|
||||
x = getkey()
|
||||
if x != -999: # A key has been pressed!
|
||||
print(characters[x-1])
|
||||
if x == 15:
|
||||
running = False
|
||||
|
||||
led.on()
|
||||
51
code-snippets/SD_Card_ESP32.py
Normal file
51
code-snippets/SD_Card_ESP32.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from machine import Pin, SPI
|
||||
import machine, sdcard, os, esp32
|
||||
|
||||
spi = SPI(2, baudrate=10000000, polarity=0, phase=0, sck=machine.Pin(18), mosi=machine.Pin(23), miso=machine.Pin(19))
|
||||
sd = sdcard.SDCard(spi, machine.Pin(5))
|
||||
|
||||
os.mount(sd, '/sd')
|
||||
|
||||
def print_directory(path, tabs = 0):
|
||||
for file in os.listdir(path):
|
||||
stats = os.stat(path+"/"+file)
|
||||
filesize = stats[6]
|
||||
isdir = stats[0] & 0x4000
|
||||
|
||||
if filesize < 1000:
|
||||
sizestr = str(filesize) + " byte"
|
||||
elif filesize < 1000000:
|
||||
sizestr = "%0.1f KB" % (filesize/1000)
|
||||
else:
|
||||
sizestr = "%0.1f MB" % (filesize/1000000)
|
||||
|
||||
prettyprintname = ""
|
||||
for i in range(tabs):
|
||||
prettyprintname += " "
|
||||
prettyprintname += file
|
||||
if isdir:
|
||||
prettyprintname += "/"
|
||||
print('{0:<40} Size: {1:>10}'.format(prettyprintname, sizestr))
|
||||
|
||||
# recursively print directory contents
|
||||
if isdir:
|
||||
print_directory(path+"/"+file, tabs+1)
|
||||
|
||||
#with open("/sd/test1.txt", "w") as f:
|
||||
# f.write("Hello world!\r\n")
|
||||
|
||||
print("Files on filesystem:")
|
||||
print("====================")
|
||||
print_directory("/sd")
|
||||
|
||||
#
|
||||
#print("Files on filesystem:")
|
||||
#print("====================")
|
||||
#print_directory("/sd")
|
||||
#
|
||||
#with open("/sd/test/test1.txt", "w") as f:
|
||||
# f.write("Hello world!\r\n")
|
||||
#
|
||||
#print("Files on filesystem:")
|
||||
#print("====================")
|
||||
#print_directory("/sd/test")
|
||||
31
scripts/init_flash.sh
Executable file
31
scripts/init_flash.sh
Executable file
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
##############################################################################
|
||||
# This script inits the esp with the miropython image
|
||||
# end some needed dependencies like the sdcard and
|
||||
# bmp180 libs
|
||||
#
|
||||
# This scripts has following dependencies:
|
||||
# - esptool.py (https://github.com/espressif/esptool)
|
||||
# - ampy (https://github.com/scientifichackers/ampy)
|
||||
#
|
||||
# The paths, files etc. may need to be configured
|
||||
# to your own environment
|
||||
#
|
||||
# Script is provided by Christopher Scheuring <chris@aucmail.de>
|
||||
# as it is. Use by your own risks.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
echo "Flush the esp32 etc. using the esptool and ampy for uploading the micropyhton libs."
|
||||
python3 /usr/local/bin/esptool.py --chip esp32 --port /dev/ttyUSB0 erase_flash
|
||||
echo "Flashing starts in 1 second..."
|
||||
sleep 1
|
||||
python3 /usr/local/bin/esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 230400 write_flash -z 0x1000 ../src/fw-bin/esp32-20220117-v1.18.bin
|
||||
echo "Flash micropython finished"
|
||||
sleep 1
|
||||
echo "Upload micropython dependency..."
|
||||
ampy -p /dev/ttyUSB0 put ../src/lib/sdcard.py
|
||||
ampy -p /dev/ttyUSB0 put ../src/lib/bmp180.py
|
||||
ampy -p /dev/ttyUSB0 ls
|
||||
echo "Flashing finished"
|
||||
10
src/fw-bin/README.md
Normal file
10
src/fw-bin/README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
Put your ESP firmware for flashing e.g. inside this dir
|
||||
|
||||
ESP32 firmware is available from the Espressif site:
|
||||
https://docs.espressif.com/projects/esp-at/en/latest/esp32/AT_Binary_Lists/ESP32_AT_binaries.html
|
||||
|
||||
ESP32 micropython firmware:
|
||||
https://micropython.org/download/esp32/
|
||||
|
||||
Infos about flashing etc:
|
||||
https://docs.espressif.com/projects/esp-at/en/latest/esp32/Get_Started/Downloading_guide.html
|
||||
187
src/lib/bmp180.py
Normal file
187
src/lib/bmp180.py
Normal file
@@ -0,0 +1,187 @@
|
||||
'''
|
||||
bmp180 is a micropython module for the Bosch BMP180 sensor. It measures
|
||||
temperature as well as pressure, with a high enough resolution to calculate
|
||||
altitude.
|
||||
Breakoutboard: http://www.adafruit.com/products/1603
|
||||
data-sheet: http://ae-bst.resource.bosch.com/media/products/dokumente/
|
||||
bmp180/BST-BMP180-DS000-09.pdf
|
||||
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2014 Sebastian Plamauer, oeplse@gmail.com
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
'''
|
||||
|
||||
from ustruct import unpack as unp
|
||||
from machine import I2C, Pin
|
||||
import math
|
||||
import time
|
||||
|
||||
# BMP180 class
|
||||
class BMP180():
|
||||
'''
|
||||
Module for the BMP180 pressure sensor.
|
||||
'''
|
||||
|
||||
_bmp_addr = 119 # adress of BMP180 is hardcoded on the sensor
|
||||
|
||||
# init
|
||||
def __init__(self, i2c_bus):
|
||||
|
||||
# create i2c obect
|
||||
_bmp_addr = self._bmp_addr
|
||||
self._bmp_i2c = i2c_bus
|
||||
#self._bmp_i2c.start() # uncomment for SoftI2C ESP usage
|
||||
self.chip_id = self._bmp_i2c.readfrom_mem(_bmp_addr, 0xD0, 2)
|
||||
# read calibration data from EEPROM
|
||||
self._AC1 = unp('>h', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xAA, 2))[0]
|
||||
self._AC2 = unp('>h', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xAC, 2))[0]
|
||||
self._AC3 = unp('>h', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xAE, 2))[0]
|
||||
self._AC4 = unp('>H', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xB0, 2))[0]
|
||||
self._AC5 = unp('>H', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xB2, 2))[0]
|
||||
self._AC6 = unp('>H', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xB4, 2))[0]
|
||||
self._B1 = unp('>h', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xB6, 2))[0]
|
||||
self._B2 = unp('>h', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xB8, 2))[0]
|
||||
self._MB = unp('>h', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xBA, 2))[0]
|
||||
self._MC = unp('>h', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xBC, 2))[0]
|
||||
self._MD = unp('>h', self._bmp_i2c.readfrom_mem(_bmp_addr, 0xBE, 2))[0]
|
||||
|
||||
# settings to be adjusted by user
|
||||
self.oversample_setting = 3
|
||||
self.baseline = 101325.0
|
||||
|
||||
# output raw
|
||||
self.UT_raw = None
|
||||
self.B5_raw = None
|
||||
self.MSB_raw = None
|
||||
self.LSB_raw = None
|
||||
self.XLSB_raw = None
|
||||
self.gauge = self.makegauge() # Generator instance
|
||||
for _ in range(128):
|
||||
next(self.gauge)
|
||||
time.sleep_ms(1)
|
||||
|
||||
def compvaldump(self):
|
||||
'''
|
||||
Returns a list of all compensation values
|
||||
'''
|
||||
return [self._AC1, self._AC2, self._AC3, self._AC4, self._AC5, self._AC6,
|
||||
self._B1, self._B2, self._MB, self._MC, self._MD, self.oversample_setting]
|
||||
|
||||
# gauge raw
|
||||
def makegauge(self):
|
||||
'''
|
||||
Generator refreshing the raw measurments.
|
||||
'''
|
||||
delays = (5, 8, 14, 25)
|
||||
while True:
|
||||
self._bmp_i2c.writeto_mem(self._bmp_addr, 0xF4, bytearray([0x2E]))
|
||||
t_start = time.ticks_ms()
|
||||
while (time.ticks_ms() - t_start) <= 5: # 5mS delay
|
||||
yield None
|
||||
try:
|
||||
self.UT_raw = self._bmp_i2c.readfrom_mem(self._bmp_addr, 0xF6, 2)
|
||||
except:
|
||||
yield None
|
||||
self._bmp_i2c.writeto_mem(self._bmp_addr, 0xF4, bytearray([0x34+(self.oversample_setting << 6)]))
|
||||
t_pressure_ready = delays[self.oversample_setting]
|
||||
t_start = time.ticks_ms()
|
||||
while (time.ticks_ms() - t_start) <= t_pressure_ready:
|
||||
yield None
|
||||
try:
|
||||
self.MSB_raw = self._bmp_i2c.readfrom_mem(self._bmp_addr, 0xF6, 1)
|
||||
self.LSB_raw = self._bmp_i2c.readfrom_mem(self._bmp_addr, 0xF7, 1)
|
||||
self.XLSB_raw = self._bmp_i2c.readfrom_mem(self._bmp_addr, 0xF8, 1)
|
||||
except:
|
||||
yield None
|
||||
yield True
|
||||
|
||||
def blocking_read(self):
|
||||
if next(self.gauge) is not None: # Discard old data
|
||||
pass
|
||||
while next(self.gauge) is None:
|
||||
pass
|
||||
|
||||
@property
|
||||
def oversample_sett(self):
|
||||
return self.oversample_setting
|
||||
|
||||
@oversample_sett.setter
|
||||
def oversample_sett(self, value):
|
||||
if value in range(4):
|
||||
self.oversample_setting = value
|
||||
else:
|
||||
print('oversample_sett can only be 0, 1, 2 or 3, using 3 instead')
|
||||
self.oversample_setting = 3
|
||||
|
||||
@property
|
||||
def temperature(self):
|
||||
'''
|
||||
Temperature in degree C.
|
||||
'''
|
||||
next(self.gauge)
|
||||
try:
|
||||
UT = unp('>H', self.UT_raw)[0]
|
||||
except:
|
||||
return 0.0
|
||||
X1 = (UT-self._AC6)*self._AC5/2**15
|
||||
X2 = self._MC*2**11/(X1+self._MD)
|
||||
self.B5_raw = X1+X2
|
||||
return (((X1+X2)+8)/2**4)/10
|
||||
|
||||
@property
|
||||
def pressure(self):
|
||||
'''
|
||||
Pressure in mbar.
|
||||
'''
|
||||
next(self.gauge)
|
||||
self.temperature # Populate self.B5_raw
|
||||
try:
|
||||
MSB = unp('B', self.MSB_raw)[0]
|
||||
LSB = unp('B', self.LSB_raw)[0]
|
||||
XLSB = unp('B', self.XLSB_raw)[0]
|
||||
except:
|
||||
return 0.0
|
||||
UP = ((MSB << 16)+(LSB << 8)+XLSB) >> (8-self.oversample_setting)
|
||||
B6 = self.B5_raw-4000
|
||||
X1 = (self._B2*(B6**2/2**12))/2**11
|
||||
X2 = self._AC2*B6/2**11
|
||||
X3 = X1+X2
|
||||
B3 = ((int((self._AC1*4+X3)) << self.oversample_setting)+2)/4
|
||||
X1 = self._AC3*B6/2**13
|
||||
X2 = (self._B1*(B6**2/2**12))/2**16
|
||||
X3 = ((X1+X2)+2)/2**2
|
||||
B4 = abs(self._AC4)*(X3+32768)/2**15
|
||||
B7 = (abs(UP)-B3) * (50000 >> self.oversample_setting)
|
||||
if B7 < 0x80000000:
|
||||
pressure = (B7*2)/B4
|
||||
else:
|
||||
pressure = (B7/B4)*2
|
||||
X1 = (pressure/2**8)**2
|
||||
X1 = (X1*3038)/2**16
|
||||
X2 = (-7357*pressure)/2**16
|
||||
return pressure+(X1+X2+3791)/2**4
|
||||
|
||||
@property
|
||||
def altitude(self):
|
||||
'''
|
||||
Altitude in m.
|
||||
'''
|
||||
try:
|
||||
p = -7990.0*math.log(self.pressure/self.baseline)
|
||||
except:
|
||||
p = 0.0
|
||||
return p
|
||||
283
src/lib/sdcard.py
Normal file
283
src/lib/sdcard.py
Normal file
@@ -0,0 +1,283 @@
|
||||
"""
|
||||
MicroPython driver for SD cards using SPI bus.
|
||||
|
||||
Requires an SPI bus and a CS pin. Provides readblocks and writeblocks
|
||||
methods so the device can be mounted as a filesystem.
|
||||
|
||||
Example usage on pyboard:
|
||||
|
||||
import pyb, sdcard, os
|
||||
sd = sdcard.SDCard(pyb.SPI(1), pyb.Pin.board.X5)
|
||||
pyb.mount(sd, '/sd2')
|
||||
os.listdir('/')
|
||||
|
||||
Example usage on ESP8266:
|
||||
|
||||
import machine, sdcard, os
|
||||
sd = sdcard.SDCard(machine.SPI(1), machine.Pin(15))
|
||||
os.mount(sd, '/sd')
|
||||
os.listdir('/')
|
||||
|
||||
"""
|
||||
|
||||
from micropython import const
|
||||
import time
|
||||
|
||||
|
||||
_CMD_TIMEOUT = const(100)
|
||||
|
||||
_R1_IDLE_STATE = const(1 << 0)
|
||||
# R1_ERASE_RESET = const(1 << 1)
|
||||
_R1_ILLEGAL_COMMAND = const(1 << 2)
|
||||
# R1_COM_CRC_ERROR = const(1 << 3)
|
||||
# R1_ERASE_SEQUENCE_ERROR = const(1 << 4)
|
||||
# R1_ADDRESS_ERROR = const(1 << 5)
|
||||
# R1_PARAMETER_ERROR = const(1 << 6)
|
||||
_TOKEN_CMD25 = const(0xFC)
|
||||
_TOKEN_STOP_TRAN = const(0xFD)
|
||||
_TOKEN_DATA = const(0xFE)
|
||||
|
||||
|
||||
class SDCard:
|
||||
def __init__(self, spi, cs, baudrate=1320000):
|
||||
self.spi = spi
|
||||
self.cs = cs
|
||||
|
||||
self.cmdbuf = bytearray(6)
|
||||
self.dummybuf = bytearray(512)
|
||||
self.tokenbuf = bytearray(1)
|
||||
for i in range(512):
|
||||
self.dummybuf[i] = 0xFF
|
||||
self.dummybuf_memoryview = memoryview(self.dummybuf)
|
||||
|
||||
# initialise the card
|
||||
self.init_card(baudrate)
|
||||
|
||||
def init_spi(self, baudrate):
|
||||
try:
|
||||
master = self.spi.MASTER
|
||||
except AttributeError:
|
||||
# on ESP8266
|
||||
self.spi.init(baudrate=baudrate, phase=0, polarity=0)
|
||||
else:
|
||||
# on pyboard
|
||||
self.spi.init(master, baudrate=baudrate, phase=0, polarity=0)
|
||||
|
||||
def init_card(self, baudrate):
|
||||
|
||||
# init CS pin
|
||||
self.cs.init(self.cs.OUT, value=1)
|
||||
|
||||
# init SPI bus; use low data rate for initialisation
|
||||
self.init_spi(100000)
|
||||
|
||||
# clock card at least 100 cycles with cs high
|
||||
for i in range(16):
|
||||
self.spi.write(b"\xff")
|
||||
|
||||
# CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts)
|
||||
for _ in range(5):
|
||||
if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE:
|
||||
break
|
||||
else:
|
||||
raise OSError("no SD card")
|
||||
|
||||
# CMD8: determine card version
|
||||
r = self.cmd(8, 0x01AA, 0x87, 4)
|
||||
if r == _R1_IDLE_STATE:
|
||||
self.init_card_v2()
|
||||
elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND):
|
||||
self.init_card_v1()
|
||||
else:
|
||||
raise OSError("couldn't determine SD card version")
|
||||
|
||||
# get the number of sectors
|
||||
# CMD9: response R2 (R1 byte + 16-byte block read)
|
||||
if self.cmd(9, 0, 0, 0, False) != 0:
|
||||
raise OSError("no response from SD card")
|
||||
csd = bytearray(16)
|
||||
self.readinto(csd)
|
||||
if csd[0] & 0xC0 == 0x40: # CSD version 2.0
|
||||
self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024
|
||||
elif csd[0] & 0xC0 == 0x00: # CSD version 1.0 (old, <=2GB)
|
||||
c_size = csd[6] & 0b11 | csd[7] << 2 | (csd[8] & 0b11000000) << 4
|
||||
c_size_mult = ((csd[9] & 0b11) << 1) | csd[10] >> 7
|
||||
self.sectors = (c_size + 1) * (2 ** (c_size_mult + 2))
|
||||
else:
|
||||
raise OSError("SD card CSD format not supported")
|
||||
# print('sectors', self.sectors)
|
||||
|
||||
# CMD16: set block length to 512 bytes
|
||||
if self.cmd(16, 512, 0) != 0:
|
||||
raise OSError("can't set 512 block size")
|
||||
|
||||
# set to high data rate now that it's initialised
|
||||
self.init_spi(baudrate)
|
||||
|
||||
def init_card_v1(self):
|
||||
for i in range(_CMD_TIMEOUT):
|
||||
self.cmd(55, 0, 0)
|
||||
if self.cmd(41, 0, 0) == 0:
|
||||
self.cdv = 512
|
||||
# print("[SDCard] v1 card")
|
||||
return
|
||||
raise OSError("timeout waiting for v1 card")
|
||||
|
||||
def init_card_v2(self):
|
||||
for i in range(_CMD_TIMEOUT):
|
||||
time.sleep_ms(50)
|
||||
self.cmd(58, 0, 0, 4)
|
||||
self.cmd(55, 0, 0)
|
||||
if self.cmd(41, 0x40000000, 0) == 0:
|
||||
self.cmd(58, 0, 0, 4)
|
||||
self.cdv = 1
|
||||
# print("[SDCard] v2 card")
|
||||
return
|
||||
raise OSError("timeout waiting for v2 card")
|
||||
|
||||
def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False):
|
||||
self.cs(0)
|
||||
|
||||
# create and send the command
|
||||
buf = self.cmdbuf
|
||||
buf[0] = 0x40 | cmd
|
||||
buf[1] = arg >> 24
|
||||
buf[2] = arg >> 16
|
||||
buf[3] = arg >> 8
|
||||
buf[4] = arg
|
||||
buf[5] = crc
|
||||
self.spi.write(buf)
|
||||
|
||||
if skip1:
|
||||
self.spi.readinto(self.tokenbuf, 0xFF)
|
||||
|
||||
# wait for the response (response[7] == 0)
|
||||
for i in range(_CMD_TIMEOUT):
|
||||
self.spi.readinto(self.tokenbuf, 0xFF)
|
||||
response = self.tokenbuf[0]
|
||||
if not (response & 0x80):
|
||||
# this could be a big-endian integer that we are getting here
|
||||
for j in range(final):
|
||||
self.spi.write(b"\xff")
|
||||
if release:
|
||||
self.cs(1)
|
||||
self.spi.write(b"\xff")
|
||||
return response
|
||||
|
||||
# timeout
|
||||
self.cs(1)
|
||||
self.spi.write(b"\xff")
|
||||
return -1
|
||||
|
||||
def readinto(self, buf):
|
||||
self.cs(0)
|
||||
|
||||
# read until start byte (0xff)
|
||||
for i in range(_CMD_TIMEOUT):
|
||||
self.spi.readinto(self.tokenbuf, 0xFF)
|
||||
if self.tokenbuf[0] == _TOKEN_DATA:
|
||||
break
|
||||
time.sleep_ms(1)
|
||||
else:
|
||||
self.cs(1)
|
||||
raise OSError("timeout waiting for response")
|
||||
|
||||
# read data
|
||||
mv = self.dummybuf_memoryview
|
||||
if len(buf) != len(mv):
|
||||
mv = mv[: len(buf)]
|
||||
self.spi.write_readinto(mv, buf)
|
||||
|
||||
# read checksum
|
||||
self.spi.write(b"\xff")
|
||||
self.spi.write(b"\xff")
|
||||
|
||||
self.cs(1)
|
||||
self.spi.write(b"\xff")
|
||||
|
||||
def write(self, token, buf):
|
||||
self.cs(0)
|
||||
|
||||
# send: start of block, data, checksum
|
||||
self.spi.read(1, token)
|
||||
self.spi.write(buf)
|
||||
self.spi.write(b"\xff")
|
||||
self.spi.write(b"\xff")
|
||||
|
||||
# check the response
|
||||
if (self.spi.read(1, 0xFF)[0] & 0x1F) != 0x05:
|
||||
self.cs(1)
|
||||
self.spi.write(b"\xff")
|
||||
return
|
||||
|
||||
# wait for write to finish
|
||||
while self.spi.read(1, 0xFF)[0] == 0:
|
||||
pass
|
||||
|
||||
self.cs(1)
|
||||
self.spi.write(b"\xff")
|
||||
|
||||
def write_token(self, token):
|
||||
self.cs(0)
|
||||
self.spi.read(1, token)
|
||||
self.spi.write(b"\xff")
|
||||
# wait for write to finish
|
||||
while self.spi.read(1, 0xFF)[0] == 0x00:
|
||||
pass
|
||||
|
||||
self.cs(1)
|
||||
self.spi.write(b"\xff")
|
||||
|
||||
def readblocks(self, block_num, buf):
|
||||
nblocks = len(buf) // 512
|
||||
assert nblocks and not len(buf) % 512, "Buffer length is invalid"
|
||||
if nblocks == 1:
|
||||
# CMD17: set read address for single block
|
||||
if self.cmd(17, block_num * self.cdv, 0, release=False) != 0:
|
||||
# release the card
|
||||
self.cs(1)
|
||||
raise OSError(5) # EIO
|
||||
# receive the data and release card
|
||||
self.readinto(buf)
|
||||
else:
|
||||
# CMD18: set read address for multiple blocks
|
||||
if self.cmd(18, block_num * self.cdv, 0, release=False) != 0:
|
||||
# release the card
|
||||
self.cs(1)
|
||||
raise OSError(5) # EIO
|
||||
offset = 0
|
||||
mv = memoryview(buf)
|
||||
while nblocks:
|
||||
# receive the data and release card
|
||||
self.readinto(mv[offset : offset + 512])
|
||||
offset += 512
|
||||
nblocks -= 1
|
||||
if self.cmd(12, 0, 0xFF, skip1=True):
|
||||
raise OSError(5) # EIO
|
||||
|
||||
def writeblocks(self, block_num, buf):
|
||||
nblocks, err = divmod(len(buf), 512)
|
||||
assert nblocks and not err, "Buffer length is invalid"
|
||||
if nblocks == 1:
|
||||
# CMD24: set write address for single block
|
||||
if self.cmd(24, block_num * self.cdv, 0) != 0:
|
||||
raise OSError(5) # EIO
|
||||
|
||||
# send the data
|
||||
self.write(_TOKEN_DATA, buf)
|
||||
else:
|
||||
# CMD25: set write address for first block
|
||||
if self.cmd(25, block_num * self.cdv, 0) != 0:
|
||||
raise OSError(5) # EIO
|
||||
# send the data
|
||||
offset = 0
|
||||
mv = memoryview(buf)
|
||||
while nblocks:
|
||||
self.write(_TOKEN_CMD25, mv[offset : offset + 512])
|
||||
offset += 512
|
||||
nblocks -= 1
|
||||
self.write_token(_TOKEN_STOP_TRAN)
|
||||
|
||||
def ioctl(self, op, arg):
|
||||
if op == 4: # get number of blocks
|
||||
return self.sectors
|
||||
Reference in New Issue
Block a user