-
Hardware Handshake
06/17/2022 at 07:38 • 0 commentsWhy Use Hardware Handshake?
I really need hardware handshake to work. Older Retro-computers running BASIC just don't support transferring data without some throttling.
Other Throttling Techniques
There's a number of other throttling techniques that could be used. In fact, one of the other methods would have to be used for a target system that does not have a hardware handshake line.
One method is to add delays after each character or each line.
For some targets setting the baud rate slow enough may work. 300 or 1200 baud works with many legacy systems. These systems were used with cassette tapes and they had to "keep up" because there is no hardware throttling.
Another method for some targets could be to wait for a character to be echoed back. For instance, on MIKBUG systems echo is an option that can be turned on. This effectively ensures the target got the character. It also cuts throughput in half.
A combination of any of these should match most Target systems. All of these could easily be built into the code and configured through the serial configuration menu.
My Target Application
My target application is to transfer data to/from the slowest Target System I have, the Multicomp Build of the UK101. The Serial interface is on J5 in the upper right of this picture. It is shown hooked up to an FTDI card.
The design is a 6502 running at 1 MHz in an FPGA. The UART in the FPGA supports RTS/CTS handshake. Technically, it only has 1 handshake out of the card but I could add an I/O line for the handshake in. The UK101 currently assumes that the Host can receive data without throttling. That itself could be an issue for saving files to the SD card, but I'm ignoring that issue for now.
If the Multicomp UK101 is ready to receive data is pulls the RTS output pin low. If the UART is "full" it pulls the line high. This tells the Host to pause or resume sending serial data into the card.
Ideally, I'd like to run at 115,200 baud with full handshakes in both directions. I'd like the SD Loader to pause sending data if the Target signals that it is not yet ready to receive data and be able to signal the target that the SD Loader is able to receive more data. There's enough pins on J5 since only one half of the 2x4 header is used. The other RTS pin could be used as a CTS signal.
The UART in the target system is a 6850 in VHDL. It supports Hardware Handshake in both directions.
Technical Excursus
None of this is traditional RTS/CTS usage. The meaning of the term has changed over the years since the RS-232 spec was written originally. Another helpful write up is here. There's a good write-up of this subject here. This ties into the question of DCE vs DTE.
Let's just ignore all of this for now and just call the RTS signal the line from the Target System that signals it is ready to receive data. CTS will be the signal from the SD Loader to the target that it is ready to receive data. Both are active low signals. Data can be sent when the line is low and should be not sent when the line is high.
Having Trouble
I'm having a rough time getting Hardware Handshake working in CircuitPython. There's some indication in some of the documents that hardware handshake should work but I'm getting errors when doing the pin assignments.
The CircuitPython UART reference page shows that the busio.UART constructor supports RTS and CTS as the following:
One concerning thing is rts/cts are listed in the parameters (lower in black) but not in the constructor (in blue). I'm wondering if this is an error in the documentation.
elif baudRate == baud9600: if handShake == NoHandshake: uart = busio.UART(board.TX, board.RX, baudrate=9600) elif handShake == HWHandshake: uart = busio.UART(board.TX, board.RX, rts=board.A2, cts=board.A3, baudrate=9600)
The error message is:
Traceback (most recent call last): File "", line 724, in File "", line 679, in loadConfig File "", line 534, in setUARTConfig ValueError: Invalid pins
Line 534 is the last line, ie:
uart = busio.UART(board.TX, board.RX, rts=board.A2, cts=board.A3, baudrate=9600)
I've tried various pin assignments for the RTS, CTS pins (D2, A2) without success.
uart = busio.UART(board.TX, board.RX, rts=board.D2, cts=board.D3, baudrate=9600)
Not sure if the cts, rts is just not supported in this CircuitPython. I can't find any examples on the web of using these pins.
Manual Workaround
Put in a "manual" workaround to make the RTS/CTS digital Input / output lines and stop sending out data if the line from the other end signals it's not ready for more data. I really hate to have to do this "manually" but feel stuck. If the problem is in CircuitPython perhaps it will be working in there.
Pin Usage
Added Pin Initialization Code
# RTSin is the signal from the target that it is ready to receive data RTSin = DigitalInOut(board.D1) RTSin.direction = Direction.INPUT RTSin.pull = Pull.DOWN # Enable out if wire is not connected # CTSout is the signal from the SD Loader that it is ready to receive data CTSout = DigitalInOut(board.D2) CTSout.direction = Direction.OUTPUT CTSout.value = False # Active low
Updated Serial out code
# uploadSerial() - Upload the selected file on the serial port def uploadSerial(): global selectedFile pathFileName = selectedFile[0] + selectedFile[1] if serialDebug: print("uploadSerial(): listing file",pathFileName) # Make sure file was selected first if pathFileName == '': errorMessage("Select file first") return # Make sure baud rate was selected if baudRate == baudUnconfig: errorMessage("Config baud rate!") return clearOLED(display) printToOLED(display,0,0,"Uploading Serial") printToOLED(display,0,1,pathFileName) updateOLEDDisplay() sendFileOutSerial(pathFileName) printToOLED(display,0,2,"Upload is Done!") updateOLEDDisplay() time.sleep(2) return
Tested by pulling the handshake up/down. Works good. Not sure how large the output buffer is. Setting the baud rate to 300 and pulling the line high/low shows several "lines" of data get sent out before it stops. This could over-run the receiver on the UART.
From the UART reference documents, it looks as if the input buffer size could be set but not the output buffer size.
classbusio.UART(tx: microcontroller.Pin, rx: microcontroller.Pin, *, baudrate: int = 9600, bits: int = 8, parity: Optional[Parity] = None, stop: int = 1, timeout: float = 1, receiver_buffer_size: int = 64)
It might be necessary to write a low-level driver to control the UART directly to really do this right.
-
Demo Video
06/16/2022 at 14:03 • 0 comments -
Storing configuration values
06/16/2022 at 09:07 • 0 commentsI'd like to store the default configuration values for the uart. At first blush it seemed like the Flash memory could be used for that function. Turns out it's a bit painful to use the Flash memory. The reason is that the Flash system gets locked by the USB when connected to the Host Computer. CircuitPython does this to avoid data corruption. This isn't an insurmountable problem because this application doesn't need to use the Serial port, but I'd still like the USB Serial for development and convenience.
The Adafruit Learning Guide for Storage shows a method with a boot.py file that reads an input pin to determine r/w state of the drive at power. It makes the Flash read-only to the Host Computer and thereby read-write to the application on the card.
I do have 1 spare input but I'd rather not use the last spare pin. If you set the boot.py to always make the drive read-only then there's no way to edit it later as the drive will always mount as read-only to the host computer.
Better solution
I think a better approach is to create a file on the SD card that loads/stores the configuration. That has the value that a target specific file would be created. I envision using different SD cards for various target systems.
Created /sd/SDLdr.cfg file to store the configuration as a comma separated list of baudRate, serProtocol, handShake.
If there's no SDLdr.cfg file on the SD it creates a file with default values:
- 9600 baud
- Serial (straight serial)
- No handshake
Created three functions:
# initConfig() - Initialize the configuration parameter values # loadConfig() - Load the configuration parameters from the SD card # storeConfig() - Store the configuration parameters to the SD card
Tested and it works! It's nice to not have to set up the COM ports every time I run the code.
Created zip file QT-Py/SDLoader_V2-20220616.zip of the current source code.
-
Added Newline to Serial Send
06/15/2022 at 18:41 • 0 commentsThe serial send routine (readPrintFileLines(pathFileName)) reads a line at a time and strips off the newline. It would be a good addition as an option but since I'm only handling ASCII serial data at the moment I added it back in.
# readPrintFileLines() Dump file to USB serial def readPrintFileLines(pathFileName): global uart with open(pathFileName, "r") as f: for line in f: if serialDebug: print(line, end='') uart.write(bytes(line, 'utf-8')) uart.write(bytes('\r', 'utf-8'))
Grabbed data using an FTDI card
Dumped a file to TeraTerm connected to the FTDI card.
Looks good.
-
Alternate Hardware
06/15/2022 at 17:08 • 0 commentsThe "unique" piece of hardware in this design is the MyMenu card.
The MyMenu card has an MCP23008 I2C port expander. This is necessary since the menu system needs at least three buttons and there are not enough pins on the QT Py or XIAO RP2040 cards to have three buttons on three separate pins.
Three Buttons on one Analog Input
A resistor tree with switches could be used to convert the three switches to different voltage levels and input the line to a single analog input - like this:
Alternate Inputs
Alternately, a Raspberry Pi Pico could be used since it has a lot of extra I/O pins. This JOYPAD card could be used with a Raspberry Pi Pico card like the PiPicoMite01.
-
Status - Working Code
06/15/2022 at 17:05 • 0 commentsGot menus working for the serial port configuration for:
- Baud rate
- Serial Protocol
- Handshake
Menus are all in place, but not all of the functions are created for the above. The baud rate select menu is working/tested and set to 9600 baud as default. Tested at 9600 baud and 300 baud.
Got UART transmit code working.
This is now a minimal viable product.
-
Changing Card and Operating System
06/13/2022 at 17:19 • 0 commentsI got the application working in the Arduino IDE with the QTPy based on the SAMD21 CPU. Then, I got to handle the directory listing and file selection. That's when things got much more painful.
If the number of files on the SD card was small enough it wouldn't be a real problem but the use-case here is where there are dozens of files. For example. I've got 165 programs that could be downloaded to my MultiComp UK101 board.
The directory contents need to be stored into a list and C/C++ just isn't all that great at handling lists because everything list related in C++ is DIY. And it just gets more painful if the lists start to get longer. because there is only 32KB of SRAM in the SAMD. I really wanted to switch over to MicroPython or CircuitPython to take advantage of easier list processing. But the SAMD just doesn't have enough internal SRAM to run either Python variant.
RP2040 to the Rescue
Fortunately, there's an RP2040 version of the QT Py. The RP2040 is the same CPU used in the Raspberry Pi Pico and it's more than capable of handling this task. There's a $5.40 version of the card by Seeed Studio. The RP2040 CPU chip itself is more widely available than the SAMD used on the original QT Py.
So I am making the switch to the XIAO RP2040 by Seeed Studio and CircuitPython. The Adafruit RP2040 card is more expensive (at $9.95) and frequently out of stock (it is at the moment). So I bought 5x of the Seeed Studio XIAO RP2040 cards to play with.
If someone wants to try the code but doesn't want to replicate the MyMenu card they could use a Raspberry Pi Pico card. There's enough more than enough Digital I/O pins on the card to replicate the switches. In fact, our PiPicoMite01 or PiPicoMite02 would do the job quite nicely.
MyMenu Example code
I re-wrote the MyMenu example code to run on CircuitPython. The example code reads the 5 pushbuttons and puts a pattern on the 3 LEDs as well as printing which button was pressed to the OLED. It works well.
The framebuff has a 5x8 character set allows 21 columns and 8 rows to characters to be displayed on the OLED.
SD Card Code
The SD card code was particularly easy to get working since the example code just worked with no issues. Adding the path and file name as pairs to a list was also easy.
Here's the result of running the code:
Files on filesystem: ==================== System Volume Information/ Size: 0 by WPSettings.dat Size: 12 by IndexerVolumeGuid Size: 76 by SuperStarTrek.bas Size: 20.0 KB scramble.bas Size: 19.2 KB CivilWar.bas Size: 13.5 KB dirFileNames [('/sd', 'System Volume Information'), ('/sd/System Volume Information', 'WPSettings.dat'), ('/sd/System Volume Information', 'IndexerVolumeGuid'), ('/sd', 'SuperStarTrek.bas'), ('/sd', 'scramble.bas'), ('/sd', 'CivilWar.bas')] ('/sd', 'System Volume Information') ('/sd/System Volume Information', 'WPSettings.dat') ('/sd/System Volume Information', 'IndexerVolumeGuid') ('/sd', 'SuperStarTrek.bas') ('/sd', 'scramble.bas') ('/sd', 'CivilWar.bas')
The first dirFileNames is as a list of pairs of (path, fileName). The next is that list line-by-line.
Organizing folders
Created separate folders and made a list of the paths.
['/sd', '/sd/a_b', '/sd/c_f', '/sd/g_l', '/sd/m_q', '/sd/r_s', '/sd/t_z']
Moved files grouped into each folder. Organized so that there's around 30 files in each folder. This should make stepping through the files easier on the OLED which only has 8 lines.
Folder, File selection working
I got the folder and file selection working with the MyMenu pushbuttons from the REPL.
Added a high-level wrapper to call the file selector, File send function (not written yet), and COM port config (not written yet).
Checked in the code (ZIP file here).
-
Directory Working
06/09/2022 at 13:49 • 0 commentsPut the code pieces together. Made a ZIP file of the current build.
Status
- Menu is functional
- Menu structure
- Top Level
- File Send
- File Receive
- Config COM
- Sd Info
- SD Dir
- Tests
- Config COM
- Set Baud Rate
- Config Handshakes
- Tests
- Test LEDs
- Test Buttons
- Top Level
To Do
- SD Dir currently just displays a directory
- Needs to allow selection of file to transfer
-
Testing Hardware/Software Pieces
06/07/2022 at 00:49 • 0 commentsCreated Two GitHub Code Development Repositories
- Python Code GitHub (not strictly needed, but helpful)
- Arduino Code GitHub
Python Test (optional)
I have a couple of pieces of Python code that I used to test the connections to the MyMenu card. I wanted to do this while I still had MicroPython (or CircuitPython) running on the QT Py card. This isn't strictly necessary because the MyMenu card connections are checked in the Arduino MyMenu example code later on but it gave me confidence I was on the right path and that the wiring was correct.
I2C Scanner (Python Test Code)
First, I ran a Python based I2C scanner to help debug the I2C connections. When I ran this it showed a list of two device addresses; one for the OLED card and the other for the MCP23008.
MyMenu Test Switches and LEDs (Python Test Code)
I wrote MyMenuPBLEDs.py Python code to test the pushbuttons and OLEDs on the MyMenu card. Pressing buttons displays button code on LEDs and it worked. But it did show some confidence in the functionality of the MyMenu card. It might be useful in the future for Python code development
Why Use Arduino IDE and not just Python?
There's a couple of reasons that I chose the Arduino environment instead of Python for this project. One is code size. The QT Py itself has plenty of Flash memory for an application like this but the RAM turns out to be really small for a decent sized Python application. And from what I can tell when Python does an import it loads the program from the Flash into the small SRAM. By contrast, the C code runs from Flash Memory.
The other is library support. I already have working menu code that works in the Arduino environment. I was really curious just how well existing libraries would play with the QT Py. I had to do a very small amount of tweeking to get the existing code to work. Really all I had to do was remove a couple of errors in the MyMenu and MCP23008 libraries that were doing a test of the CPU architecture type. This code was used to set the I2C speed for the different CPU types. Removing it won't be an issue for other applications. It's tough to figure out the defines for various architectures. This code was used to set the I2C speed and it seems to have been fixed in the Arduino environment with:
Wire.setClock(400000);
Setup Arduino IDE support for the QT Py
Followed Adafruit's directions here. Had no issues getting it working. The download process is really simple and doesn't require any button presses which is great since I can't get to button in my board stack-up.
I2C Scanner (from Arduino IDE)
Ran i2c_scanner_wire Arduino code to check the connections to the MyMenu card. Got back the expected result on the USB Serial Monitor:
Scanning... I2C device found at address 0x20 I2C device found at address 0x3C done
This shows the OLED at 0x3c and the MCP23008 at 0x20. Both of these are on the MyMenu card.
Why Use the MyMenu Card?
There's just not enough spare pins on the QT Py to read 5 switches. The I2C pins are "free" since they are used already for the OLED. As long as the OLED and MCP23008 I2C addresses don't collide (which they don't) there's no problem. Plus the 3 output LEDs could be used for status.
In fact, there no extra pins free on the QT Py. However, if the INT* line from the MyMenu card wasn't used there would be a free pin. This pin could be used as an analog input and the switches could be connected to these pins with a resistor chain. The QT Py could read the voltage and determine which key was pressed. That is illustrated as something like this (using 3.3V instead of 5V and five switches instead of 6):
I didn't do this since I have the MyMenu card.
Test SD Card
Ran SD_Dir.ino in Arduino code to dump the directory of the SD card. Only needed to change line that sets the Chip Select to pin 3:
const int chipSelect = 3;
Got back:
Initializing SD card...initialization done. SYSTEM~1/ WPSETT~1.DAT 12 INDEXE~1 76 done!
It worked!
This displays the System files which are on a "blank" but FAT32 formatted SD card.
SD Card Info
Ran CardInfo.ino code using a "bland but formatted" 8GB SanDisk card. Got back:
Initializing SD card...Wiring is correct and a card is present. Card type: SDHC Clusters: 1936384 Blocks x Cluster: 8 Total Blocks: 15491072 Volume type is: FAT32 Volume size (Kb): 7745536 Volume size (Mb): 7564 Volume size (Gb): 7.39 Files found on the card (name, date and size in bytes): SYSTEM~1/ 2022-05-14 18:07:06 WPSETT~1.DAT 2022-05-14 18:07:06 12 INDEXE~1 2022-05-14 18:07:10 76
This is what I expected. There's some system files on the SD card and nothin else.
Test MyMenu code in Arduino IDE
Ran OLEDMenuCodeV2 code. This is a sample menu program that will be the base of the project. It worked with minor tweeks to the library (described above). The OLED displayed menus. Built-in buttons test option passed.
Test Serial Port
This was a small challenge. The Adafruit M0 library uses the USB for serial. I needed to use Serial1 to write out the actual serial pins instead of the USB serial. I searched the Adafruit notes and forums to find an answer. In the end I just "guessed" that the hardware pins are Serial1 and they were. Ran DigitalReadSerial code with minor tweeks to use Serial1 instead of Serial and to use Pin 2 as the input. Pin 2 is going to be used as the hardware handshake into the QT Py from the target system.
It worked. When I pulled the line high it showed a "1" on the Serial port at 9600 baud, and when I pulled it low it showed a "0". Nice to know that the serial out pin is working and the hardware handshake input pin is reading a bit.
Read a File, Write out Serial
The next step is to read a file from the SD card and write it out the Serial port. I used the DumpFile example code with a couple of simple changes:
// Select the Serial port to send output to / #define THESERIAL Serial1 // UART pins #define THESERIAL Serial // USB Serial const int chipSelect = 3; // SD card Chip Select
The two defines determine which serial port is used for output.
- Serial1 uses the hardware UART pins
- Serial uses the USB Serial
Using the USB Serial for debug.
Loaded a file to the card (CivilWar.bas).
Result is:
The first line should be sent to the OLED as an status message or error message if there's an issue with the SD card access.
Re-Running SD_Dir
Shows the files that I loaded to the SD card are as expected:
Results
This should be all that is needed to write the actual SD Loader application. Should just need to put the pieces together and write a menu program.