-
-
Save ataffanel/03b45482deb5c8b5f90b41dc9051d003 to your computer and use it in GitHub Desktop.
Program nRF24LU1 bootloader using Raspberrypi GPIO as SPI programmer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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