{"id":24,"date":"2020-05-15T20:20:46","date_gmt":"2020-05-15T20:20:46","guid":{"rendered":"http:\/\/hindsbo.dk\/blog\/?p=24"},"modified":"2020-05-18T15:33:01","modified_gmt":"2020-05-18T15:33:01","slug":"spi-part-2","status":"publish","type":"post","link":"http:\/\/hindsbo.dk\/blog\/?p=24","title":{"rendered":"SPI Part 2"},"content":{"rendered":"\n<p>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 <a href=\"http:\/\/westerndesigncenter.com\/wdc\/documentation\/w65c22.pdf\">VIA documentation<\/a> for more details)<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">VIA1_PORTB      = $F100      ; address of port a\nVIA1_PORTA      = $F101      ; address of port b\nVIA1_DDRB       = $F102      ; data direction register for port a\nVIA1_DDRA       = $F103      ; data direction register for port b\nVIA1_T1CL       = $F104      ; timer 1 counter low \nVIA1_T1CH       = $F105      ; timer 1 counter high\nVIA1_T1LL       = $F106      ; timer 1 latch low\nVIA1_T1LH       = $F107      ; timer 1 latch high\nVIA1_T2CL       = $F108      ; timer 2 counter low \nVIA1_T2CH       = $F109      ; timer 2 counter high\nVIA1_SR         = $F10A      ; shift register \nVIA1_ACR        = $F10B      ; auxiliary control register \nVIA1_PCR        = $F10C      ; peripheral control register \nVIA1_IFR        = $F10D      ; interrupt flag register \nVIA1_IER        = $F10E      ; interrupt enable register\nVIA1_ORA        = $F10F      ; same as port A, except \"no handshake\"<\/pre>\n\n\n\n<p>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) . <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">spi.init        lda #%00000100  ; disable shift-register interrupt\n                sta VIA1_IER     \n        \n                lda #$FF        ; set port B to output\n                sta VIA1_DDRB\n        \n                lda #SPI_CLKN   ; set number of counts for T2\/clock\n                sta VIA1_T2CL\n\n                rts           <\/pre>\n\n\n\n<p>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 &#8220;main thread&#8221;. 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.   <\/p>\n\n\n\n<p>Since we are using the shift-register for both MOSI and MISO communication (see my <a href=\"http:\/\/hindsbo.dk\/blog\/?p=21\">SPI Part 1 <\/a>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). <\/p>\n\n\n\n<p>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 &#8230; I know I really should implement this in HW). This will enable or disable the respective gates on the buffer. <\/p>\n\n\n\n<p>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. <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">spi.set_input   lda VIA1_ACR\n                and #%11100011    ; mask out SR control bits\n                ora #%00000100    ; SR in under control of T2 \n                sta VIA1_ACR          \n     \n                lda #%00111010    ; buffer input &amp; SD card CS = 0 \n                sta VIA1_PORTB          \n                rts \n\n                \nspi.set_output  lda VIA1_ACR\n                and #%11100011    ; mask out SR control bits\n                ora #%00010100    ; SR in under control of T2\n                sta VIA1_ACR\n\n                lda #%00111001    ; buffer input &amp; SD card CS = 0\n                sta VIA1_PORTB\n                rts\n<\/pre>\n\n\n\n<p>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. <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">spi.send_byte   sta VIA1_SR      ; send data to SR (also clears SR flag in IFR)\n@wait           lda VIA1_IFR     ; check IFR flags\n                and #%00000100   ; isolate SR flag  \n                beq @wait        ; wait until done sending byte \n                rts\n                \n\nspi.get_byte    lda VIA1_IFR     ; check IFR flag\n                and #%00000100   ; isolate SR flag  \n                beq spi.get_byte ; wait until SR flag is set (when previous shift operation is completed)\n                lda VIA1_SR      ; get data (also clears SR flag in IFR)\n                rts <\/pre>\n\n\n\n<p>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. <\/p>\n\n\n\n<p>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.  <\/p>\n\n\n\n<p>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&#8217;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). <\/p>\n\n\n\n<p>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.    <\/p>\n\n\n\n<p>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.   <\/p>\n\n\n\n<p>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&#8217;s very welcome. From my C64 coding I have been using the excellent <a href=\"https:\/\/georg-rottensteiner.de\/en\/c64.html\">C64Studio<\/a> from  Georg Rottensteiner. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 &hellip; <\/p>\n<p class=\"link-more\"><a href=\"http:\/\/hindsbo.dk\/blog\/?p=24\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;SPI Part 2&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[4],"tags":[5,6],"_links":{"self":[{"href":"http:\/\/hindsbo.dk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/24"}],"collection":[{"href":"http:\/\/hindsbo.dk\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/hindsbo.dk\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/hindsbo.dk\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/hindsbo.dk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=24"}],"version-history":[{"count":6,"href":"http:\/\/hindsbo.dk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/24\/revisions"}],"predecessor-version":[{"id":30,"href":"http:\/\/hindsbo.dk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/24\/revisions\/30"}],"wp:attachment":[{"href":"http:\/\/hindsbo.dk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=24"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/hindsbo.dk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=24"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/hindsbo.dk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=24"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}