Skip to content

Instantly share code, notes, and snippets.

@ataffanel
Last active October 6, 2021 11:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ataffanel/03b45482deb5c8b5f90b41dc9051d003 to your computer and use it in GitHub Desktop.
Save ataffanel/03b45482deb5c8b5f90b41dc9051d003 to your computer and use it in GitHub Desktop.
Program nRF24LU1 bootloader using Raspberrypi GPIO as SPI programmer
#!/usr/bin/env python3
# Write a bootloader from page 60 (address 0x7800) and write the jump
# instruction page 0
# This script should work on any Raspberry pi running a recent version of
# rasbian. After the Crazyradio is connected to the raspberry pi expansion
# port just run the script with
# "python3 write_bootloader_raspi.py <bootloader.bin>"
# Script setup. Use this as a documentation to cable the Crazyradio or modify
# if you want to cable it differently.
# Name Pin on raspi Pin on Crazyradio
#-------------------------------------------
GND = 6 # 9
RESET = 3 # 3
PROG = 5 # 2
SCK = 7 # 4
MOSI = 8 # 6
MISO = 10 # 8
CS = 12 # 10
import RPi.GPIO as GPIO
import time
import sys
CS_ENABLE = GPIO.LOW
CS_DISABLE = GPIO.HIGH
# SPI commands
WREN = 0x06
WRDIS = 0x04
ERASE_PAGE = 0x52
PROGRAM = 0x02
READ = 0x03
def init_gpios():
GPIO.setmode(GPIO.BOARD)
GPIO.setup(RESET, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(PROG, GPIO.OUT, initial=GPIO.HIGH)
def check_connection():
# If reset is connected, it will be pulled up
return GPIO.input(RESET) != 0
def reset_in_prog():
GPIO.output(RESET, GPIO.LOW)
GPIO.output(PROG, GPIO.HIGH)
time.sleep(0.1)
GPIO.output(RESET, GPIO.HIGH)
time.sleep(0.1)
def reset_in_fw():
GPIO.output(RESET, GPIO.LOW)
GPIO.output(PROG, GPIO.LOW)
time.sleep(0.1)
GPIO.output(RESET, GPIO.HIGH)
time.sleep(0.1)
def spi_oe(enable):
if enable:
GPIO.setup([SCK, MOSI], GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(CS, GPIO.OUT, initial=CS_DISABLE)
GPIO.setup(MISO, GPIO.IN)
else:
GPIO.setup([SCK, MOSI, CS], GPIO.IN)
def set_cs(value):
GPIO.output(CS, value)
time.sleep(0.1)
def spi_transfer(dataout):
datain = 0
GPIO.output(SCK, GPIO.LOW)
for i in range(8):
b = GPIO.LOW
if dataout & 0x0080 != 0:
b = GPIO.HIGH
GPIO.output(MOSI, b)
dataout = dataout << 1
time.sleep(0.001)
b = GPIO.input(MISO)
GPIO.output(SCK, GPIO.HIGH)
datain = datain << 1
if b != GPIO.LOW:
datain = datain | 0x01
time.sleep(0.001)
GPIO.output(SCK, GPIO.LOW)
return datain
def erase_page(page_number):
assert(page_number >= 0 and page_number < 64)
set_cs(CS_ENABLE)
spi_transfer(WREN)
set_cs(CS_DISABLE)
set_cs(CS_ENABLE)
spi_transfer(ERASE_PAGE)
spi_transfer(page_number)
set_cs(CS_DISABLE)
time.sleep(0.1)
def program_page(page_number, data):
assert(page_number >= 0 and page_number < 64)
assert(len(data) <= 512)
start_address = page_number * 512
# Write page 256 bytes at a time
for address in range(start_address, start_address + len(data), 256):
set_cs(CS_ENABLE)
spi_transfer(WREN)
set_cs(CS_DISABLE)
set_cs(CS_ENABLE)
spi_transfer(PROGRAM)
# Address
spi_transfer(address >> 8)
spi_transfer(address &0x0FF)
# Write data
for _ in range(min(len(data),256)):
spi_transfer(data[0])
data = data[1:]
set_cs(CS_DISABLE)
# Wait for data to be written
time.sleep(1)
def verify_page(page_number, data):
assert(page_number >= 0 and page_number < 64)
assert(len(data) <= 512)
verification_ok = True
start_address = page_number * 512
set_cs(CS_ENABLE)
spi_transfer(READ)
spi_transfer(start_address >> 8)
spi_transfer(start_address & 0x0FF)
for d in data:
verification_ok &= spi_transfer(0x00) == d
set_cs(CS_DISABLE)
return verification_ok
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: {} <bootloader_bin_file.bin>".format(sys.argv[0]))
sys.exit(1)
with open(sys.argv[1], 'rb') as fd:
bootloader = fd.read()
n_pages = int((len(bootloader)-1) / 512) + 1
init_gpios()
reset_in_prog()
spi_oe(True)
print("Erasing page 0...")
erase_page(0)
print("Writing 'JL 0x7800' instruction on page 0...")
program_page(0, [0x02, 0x78, 0x00])
for page in range(60, 60 + n_pages):
file_page = page - 60
print("Erasing page {}...".format(page))
erase_page(page)
print("Writing bootloader on page {} ...".format(page))
program_page(page, bootloader[file_page*512:(file_page+1)*512])
print("Verifying page 0...")
if verify_page(0, [0x02, 0x78, 0x00]):
print("OK!")
else:
print("Failed!")
for page in range(60, 60 + n_pages):
file_page = page - 60
print("Verifying page {}...".format(page))
if verify_page(page, bootloader[file_page*512:(file_page+1)*512]):
print("OK!")
else:
print("Failed!")
spi_oe(False)
reset_in_fw()
GPIO.cleanup()
print("Bootloader programmed. Disconnect the PROG wire, reconnect Crazyradio")
print("and the bootloader should start!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment