In this post we will take a look at the basic SPI code and then subsequently at the more complex SD card and FAT file format implementation. Before we get going some definitions. The way my address decoding logic is set up the VIA registers have the following addresses and names that I will use in the code. Should be easy to modify for your system (see VIA documentation for more details)
VIA1_PORTB = $F100 ; address of port a VIA1_PORTA = $F101 ; address of port b VIA1_DDRB = $F102 ; data direction register for port a VIA1_DDRA = $F103 ; data direction register for port b VIA1_T1CL = $F104 ; timer 1 counter low VIA1_T1CH = $F105 ; timer 1 counter high VIA1_T1LL = $F106 ; timer 1 latch low VIA1_T1LH = $F107 ; timer 1 latch high VIA1_T2CL = $F108 ; timer 2 counter low VIA1_T2CH = $F109 ; timer 2 counter high VIA1_SR = $F10A ; shift register VIA1_ACR = $F10B ; auxiliary control register VIA1_PCR = $F10C ; peripheral control register VIA1_IFR = $F10D ; interrupt flag register VIA1_IER = $F10E ; interrupt enable register VIA1_ORA = $F10F ; same as port A, except "no handshake"
My initialization code disables the shift register interrupt, sets port B to output and sets the T2 clock frequency (for the fastest speed set SPI_CLKN = 0) .
spi.init lda #%00000100 ; disable shift-register interrupt sta VIA1_IER lda #$FF ; set port B to output sta VIA1_DDRB lda #SPI_CLKN ; set number of counts for T2/clock sta VIA1_T2CL rts
You can set an interrupt to trigger when 8 bits have been sent/received, but I have chosen to have the load/save running in the “main thread”. I have certain kernel update routines in IRQ (e.g. updating screen, cock, etc.) that will then interrupt when needed. Hence the SR interrupt is disabled. You could easily chose to do this differently.
Since we are using the shift-register for both MOSI and MISO communication (see my SPI Part 1 post) we need to set up both the SR register and the buffer chip for either output or input. For the SR register we do this by setting bits 2-4 in the VIA auxiliary control register (ACR) to either %001 (shift in under control of T2) or %101 (shift out under control of T2).
The the input line (connected to PB0) or the output line (connected to PB1) on the buffer is enabled by pulling it low (obviously only pull one low and the other high … I know I really should implement this in HW). This will enable or disable the respective gates on the buffer.
Finally pull the SD cards Slave Select low to indicate it is the device we are communicating to (SS1 is connected to PB2). E.g. writing %xxxxxx10 to Port B will disable output (bit 1 = 1) and enable input (bit 0 = 0). And %xx1110xx will set SS1 = 0 to select the SD card and deselect SS2-4 by setting them to 1.
spi.set_input lda VIA1_ACR and #%11100011 ; mask out SR control bits ora #%00000100 ; SR in under control of T2 sta VIA1_ACR lda #%00111010 ; buffer input & SD card CS = 0 sta VIA1_PORTB rts spi.set_output lda VIA1_ACR and #%11100011 ; mask out SR control bits ora #%00010100 ; SR in under control of T2 sta VIA1_ACR lda #%00111001 ; buffer input & SD card CS = 0 sta VIA1_PORTB rts
So far all we have done is to set up things and we need the code to actually send a byte over SPI. Note that any reading or writing to the shift register (SR) clears the respective flag in the IFR. Once 8 bits have been sent or received the flag (bit 2 in IFR) is set to signal that data is ready and if the interrupt is enabled it will also trigger an interrupt.
spi.send_byte sta VIA1_SR ; send data to SR (also clears SR flag in IFR) @wait lda VIA1_IFR ; check IFR flags and #%00000100 ; isolate SR flag beq @wait ; wait until done sending byte rts spi.get_byte lda VIA1_IFR ; check IFR flag and #%00000100 ; isolate SR flag beq spi.get_byte ; wait until SR flag is set (when previous shift operation is completed) lda VIA1_SR ; get data (also clears SR flag in IFR) rts
You have to be a little careful as you can only clear the SR flag in IFR by reading or writing to the shift register. For SD card communication you wilI always send bytes before receiving (more about this in the next blog post where we will dive into the actual SD card and FAT format). You will notice that my send_byte function sends first and then waits (until the byte has been sent), whereas the get_byte function waits first and then sends. This works because of this sequence, but if you are not careful you could be caught in a loop waiting for a flag that is never set or cleared.
Ideally you want to do other things while the SR register is sending/receiving, so for sending the above can certainly be optimized. Later you will see in some of my code where I send or receive large quantities of data that I have made these optimizations.
At the maximal T2 clock speed it will take 32 clock cycles to send/receive a byte, which can conveniently be used to e.g. storing the previous byte in memory. If you count the cycles of the code you execute between reads and writes you wouldn’t even have to check the SR flag before triggering the next byte to be sent/received (but if you are not careful you could trigger the shift register before it is ready for the next byte).
These were the basics of sending and receiving bytes over SPI and in my next blog posts I will go over the specifics of communicating with an SD card in SPI mode.
Note 1: As I was writing this blog post I realized the maximum read/write speed is attained by driving the SPI clock signal from PHI2 rather than Timer 2. The speed is then fixed at half the system clock speed and sending/receiving a byte takes 16 clock cycles, rather than 32 or more with T2. To do this you simply set bits 2-4 of ACR to %010 for receiving and %110 for sending. I tested this with my SD card and it works nicely . However if you need variable speeds for SPI communication with other devices (that might not be as fast) you still want to be able to change the SPI clock speed through T2.
Note 2: The IDE I used for coding supports standard 6502 instructions, but not the extended 65C02 instruction set, so there are places where my code could be optimized. This is a project for me to begin at some point. Suggestions for good 65C02 compilers and/or IDE’s very welcome. From my C64 coding I have been using the excellent C64Studio from Georg Rottensteiner.
Mark, this is very interesting work for me as my project is behind yours so I can follow your ideas. Thanks for writing it up so clearly – the way you describe the software steps is very helpful because then it is possible to modify it for a different system. It also is then possible to experiment with different settings or code.
I like the weekly updates – something to look forward to 🙂
Thanks again for the kind comments Robin!
Hi Mark!
Nice work, trying to follow along.
Now, i noticed that the shift clock on CB1 of the 6522 is active low (so default high, clock pulse is low), which would be okay fo SPI Mode 3 (CPOL=1), but not for Mode 1 (CPOL=0)
Am i correct in this?
As such, “some” SD cards will work, some don’t.
Also, devices requiring Mode 0 will not work either.
Is there a way to reverse the clock polarity? (other than using an inverter, ofcourse)
Greetings,
Yes I believe you are correct and I had forgotten to write about that aspect. I am not aware of a way to change this for the shift clock. You could generate a “manual” clock signal using one of the pins from one of the ports on the VIA to simulate all modes, but of course that will significantly decrease the speed of the transmission.
Good write-up, I抦 normal visitor of one抯 website, maintain up the nice operate, and It is going to be a regular visitor for a long time.
Howdy! This is kind of off topic but I need some guidance from an established blog. Is it very hard to set up your own blog? I’m not very techincal but I can figure things out pretty quick. I’m thinking about setting up my own but I’m not sure where to begin. Do you have any tips or suggestions? With thanks
I simply used WordPress and it was quite easy. I’m sure there must be a ton of great content on this topic on YouTube. Good luck!
Hi there just wanted to give you a quick heads up and let you know a few of the images aren’t loading correctly. I’m not sure why but I think its a linking issue. I’ve tried it in two different browsers and both show the same outcome.
I like the valuable information you supply to your articles. I抣l bookmark your blog and test once more here frequently. I am reasonably sure I抣l learn many new stuff right right here! Best of luck for the next!
Wow! Thank you! I permanently needed to write on my website something like that. Can I include a portion of your post to my website?
I am now not positive where you’re getting your info, but good topic. I needs to spend a while learning more or figuring out more. Thanks for excellent information I was searching for this info for my mission.
I really like your blog.. very nice colors & theme. Did you create this website yourself or did you hire someone to do it for you? Plz respond as I’m looking to construct my own blog and would like to know where u got this from. thank you
it is a standard WordPress template, so credit go to the developers around it