baantonia
Posts: 63
Joined: Fri Feb 06, 2015 2:19 pm

SPI data transfer issue

Fri Jul 15, 2016 5:00 pm

I'm using assembler code to connect either a pi-zero or a B+ to a Gertboard with its mcp3002 ADC chip. So far using an oscilloscope the clock and cs lines are operating correctly between 3.3V and 0V but the MOSI line goes to a maximum of about 3mV, which is probably too low for the ADC chip to detect. I'm currently using the polled method as described within the BCM2835 documentation. I've tested the GPIO pin states by displaying the output using the Gertboard LEDs, all the SPI pins are set to an ALT0 state. I've also tried different clock rates and different timings when the data should be transferred, all to no avail. However using the same method, and an operating system I've managed to get this to work. I suspect the 3mV fluctuation is just some interference rather than a valid signal.

Any clues or things to test would be greatly appreciated.

User avatar
joan
Posts: 15112
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: SPI data transfer issue

Fri Jul 15, 2016 5:13 pm

Why not just copy one of the C SPI implementations? The bcm2853 library and my pigpio library both talk direct to the hardware and should be easily portable (I'd have to check but I doubt any Linux calls are made within the SPI routines themselves, apart from perhaps a delay).

User avatar
mikronauts
Posts: 2817
Joined: Sat Jan 05, 2013 7:28 pm
Contact: Website

Re: SPI data transfer issue

Fri Jul 15, 2016 6:16 pm

You have almost certainly have not configured the MOSI pin correctly, perhaps by leaving it as a digital output, driven low, or as a digital input, with a pull-down.

As joan suggests, use an existing C library, or see how one initializes the pins.
baantonia wrote:I'm using assembler code to connect either a pi-zero or a B+ to a Gertboard with its mcp3002 ADC chip. So far using an oscilloscope the clock and cs lines are operating correctly between 3.3V and 0V but the MOSI line goes to a maximum of about 3mV, which is probably too low for the ADC chip to detect. I'm currently using the polled method as described within the BCM2835 documentation. I've tested the GPIO pin states by displaying the output using the Gertboard LEDs, all the SPI pins are set to an ALT0 state. I've also tried different clock rates and different timings when the data should be transferred, all to no avail. However using the same method, and an operating system I've managed to get this to work. I suspect the 3mV fluctuation is just some interference rather than a valid signal.

Any clues or things to test would be greatly appreciated.
http://Mikronauts.com - home of EZasPi, RoboPi, Pi Rtc Dio and Pi Jumper @Mikronauts on Twitter
Advanced Robotics, I/O expansion and prototyping boards for the Raspberry Pi

baantonia
Posts: 63
Joined: Fri Feb 06, 2015 2:19 pm

Re: SPI data transfer issue

Fri Jul 15, 2016 10:06 pm

There is no C library or Linux, all in pure assembler. Routines which access the hardware directly are written to use individual svc/swi calls (cf RISCOS, but no RISCOS in sight!) and each read or write to the GPIO or SPI memory areas have a data memory barrier (I hope). The pin modes are correct '0b100', this I checked this afternoon using my Gertboard LEDs, bit difficult debugging code just using 8 LEDs and shifting bits to analyse 32bit words. The SPI clock signal is working fine (via oscilloscope), just the MOSI signal is being problematic. The TA, DONE, TXD and RXD all appear to be working correctly, reading the CS memory location and acting on the status of the bits, just no data being passed, curious.

Libraries and initialisation code not shown below

Code: Select all

.include "h\\gpio.h"
.include "h\\spi.h"

.section .text

.equ    adc_temp, 0
.equ    adc_val,  4

.global _main

_main:  swi      GPIO_SetByteDisplayOut @ Set spare 8 GPIO pins for output as LED display on the Gertboard
        mov      r3, #0 @ Used to track odd and even passes
        mov      r1, #0 @ Initialise heap variables
        str      r1, [ip, #adc_temp]
        str      r1, [ip, #adc_val]

        @ Set mode of GPIO pins 7, 8, 9, 10 and 11 to 0b100
        mov      r1, #GPIO_MODE_ALT_0
        mov      r0, #7
        swi      GPIO_SetPinMode @ GPIO pin in r0, mode in r1
        mov      r1, #GPIO_MODE_ALT_0
        mov      r0, #8
        swi      GPIO_SetPinMode @ GPIO pin in r0, mode in r1
        mov      r1, #GPIO_MODE_ALT_0
        mov      r0, #9
        swi      GPIO_SetPinMode @ GPIO pin in r0, mode in r1
        mov      r1, #GPIO_MODE_ALT_0
        mov      r0, #10
        swi      GPIO_SetPinMode @ GPIO pin in r0, mode in r1
        mov      r1, #GPIO_MODE_ALT_0
        mov      r0, #11
        swi      GPIO_SetPinMode @ GPIO pin in r0, mode in r1

        mov      r0, #256 @ clock divisor
        swi      SPI_WriteSpiClk @ SPI clock divisor on r0

        @ Some debugging code
        @swi       GPIO_GetGpioAddress @ Returns GPIO address in r0
        @ldr       r0, [r0, #0x34]
        @lsr       r0, r0, #7
        @swi       GPIO_SetByteDisplay @ Routine to display lowest 8 bits of r0 usin LEDs

        swi      SPI_ReadSpiCs @ Returns in r0
        bic      r0, r0, #1<<SPI_CS_TA    @ Clear TA and DONE bits
        bic      r0, r0, #1<<SPI_CS_CSPOL @ Set CSPOL low
        orr      r0, r0, #(SPI_CS_CPOL_HIGH<<SPI_CS_CPOL|SPI_CS_CPHA_BEGIN<<SPI_CS_CPHA) @ Set CPOL and CPHA as high
        bic      r0, #0b11   @ Use ADC chip select 0
        swi      SPI_WriteSpiCs @ Writes from r0

7:      swi      SPI_ReadSpiCs @ Returns in r0
        orr      r0, r0, #(SPI_CS_TA_ACTIVE<<SPI_CS_TA) @#(SPI_CS_CLEAR_BOTH<<SPI_CS_CLEAR|SPI_CS_TA_ACTIVE<<SPI_CS_TA) @ Clear FIFO 
        swi      SPI_WriteSpiCs @ Writes from r0

1:      swi      SPI_ReadSpiCsTxd @ Returns TXD bit into r0 [0|1]
        cmp      r0, #0
        beq      1b
        mov      r0, #0xd0
        swi      SPI_WriteSpiByte @ Writes lowest 8 bits of r0 to FIFO
        
2:      swi      SPI_ReadSpiCsTxd @ Returns TXD bit into r0 [0|1]
        cmp      r0, #0
        beq      2b
        mov      r0, #0x00
        swi      SPI_WriteSpiByte @ Writes lowest 8 bits of r0 into FIFO
3:      swi      SPI_ReadSpiCsDone @ Reads DONE bit into r0 [0|1]
        cmp      r0, #0
        beq      3b
        mov      r1, #0
4:      swi      SPI_ReadSpiByte @ Reads FIFO byte into r0
        cmp      r1, #0
        bne      5f
        mov      r1, #1
        @ odd byte read msb
        mov      r2, #0
        and      r0, r0, #7
        and      r2, r2, r0, lsl #7
        str      r2, [ip, #adc_temp]
        b        6f        
5:      mov      r1, #0
        @ even byte read lsb
        ldr      r2, [ip, #adc_temp]
        orr      r2, r2, r0, lsr #1
        str      r2, [ip, #adc_val]
        mov      r0, r2
        swi      GPIO_SetByteDisplay @ Display lowest 8 bits of r0 on the LEDs
6:      swi      SPI_ReadSpiCsRxd @ Reads RXD bit [0|1]
        cmp      r0, #0
        bne      4b

        swi      SPI_ReadSpiCs @ Returns in r0
        bic      r0, r0, #1<<SPI_CS_TA @ Clear the TA bit
        swi      SPI_WriteSpiCs @ Writes from r0
        @mov      r0, #0
        @swi      SPI_WriteSpiCsTa @ Set TA bit [0|1] from r0

        add      r3, r3, #1
        @ Some debugging code
        @mov      r0, r3
        @swi      GPIO_SetByteDisplay @ Display lowest 8 bits of r0 on the LEDs
        b        7b

@ The following is unused in the above 
display_ram:
        stmfd    sp!, {r0, lr}
        swi      GPIO_GetGpioAddress @ Returns the GPIO base address in r0
        ldr      r0, [r0, #0x34] @ Fetch the contents of memory
        lsr      r0, r0, #7
        swi      GPIO_SetByteDisplay @ Display lowest 8 bits of r0 on the LEDs
        ldmfd    sp!, {r0, lr}
        mov      pc, lr

I'll check again. It's probably something simple which I just cannot see at the moment.

baantonia
Posts: 63
Joined: Fri Feb 06, 2015 2:19 pm

Re: SPI data transfer issue

Mon Jul 18, 2016 9:01 am

Checked the settings after the initial setup before the SPI poll routine, see below. I'll check the settings of pins 7-11 after polling just in case something gets changed and get back.

GPFSEL0=0x24801000
GPFSEL1=0x01224024
So all GPIO pins 7-11 are set to 0b100

Just checked to see if the pin modes change after 1 pass through the poll loop, they remain unchanged.

However looking at the read and write byte calls in the spi.s file on changing back the str and ldr instructions to strb and ldrb respectively, I get a better MOSI and MISO signals using an oscilloscope but still the transition voltage is so small the signal doesn't seem to be detected by the BCM2835 chip

Edit: The MOSI signal is not in the same shape as the bit pattern written to the FIFO so I'm assuming the signal I'm getting is background noise from somewhere.

Is there somewhere where the pin levels are adjusted?
Attachments
code.zip
This is the latest code with the batch file and linker settings I use to assemble the code. Ignore framebuffer and uart, these are work in progress and are untested.
(13.13 KiB) Downloaded 68 times

baantonia
Posts: 63
Joined: Fri Feb 06, 2015 2:19 pm

Re: SPI data transfer issue

Wed Jul 20, 2016 10:07 am

Discovered my issue!

The default value for the REN bit in the CS register is 1, which if left as it is sets the the SPI0 in bidirectional master mode whereas I'm using the polling method for standard mode. After changing the REN bit to 0 on setup, the MOSI and MISO pins transition between the correct voltages, sending and receiving data.

Thought it would be something simple I'd forgotten.

The final adc.s code is:

Code: Select all

.include "h\\gpio.h"
.include "h\\spi.h"

.section .text

.equ    adc_temp, 0
.equ    adc_val,  4
.equ    pass,     8

.global _main

_main:  swi      GPIO_SetByteDisplayOut @ Set spare 8 GPIO pins for output as LED display on the Gertboard
        mov      r1, #0 @ Initialise heap variables
        str      r1, [ip, #adc_temp]
        str      r1, [ip, #adc_val]
        str      r1, [ip, #pass]

        @ Set mode of GPIO pins 7, 8, 9, 10 and 11 to 0b100
        mov      r1, #GPIO_MODE_ALT_0
        mov      r0, #7
        swi      GPIO_SetPinMode @ GPIO pin in r0, mode in r1
        mov      r1, #GPIO_MODE_ALT_0
        mov      r0, #8
        swi      GPIO_SetPinMode @ GPIO pin in r0, mode in r1
        mov      r1, #GPIO_MODE_ALT_0
        mov      r0, #9
        swi      GPIO_SetPinMode @ GPIO pin in r0, mode in r1
        mov      r1, #GPIO_MODE_ALT_0
        mov      r0, #10
        swi      GPIO_SetPinMode @ GPIO pin in r0, mode in r1
        mov      r1, #GPIO_MODE_ALT_0
        mov      r0, #11
        swi      GPIO_SetPinMode @ GPIO pin in r0, mode in r1

        mov      r0, #256 @ clock divisor
        swi      SPI_WriteSpiClk @ SPI clock divisor on r0

        swi      SPI_ReadSpiCs @ Returns in r0
        @bic      r0, r0, #1<<SPI_CS_TA    @ Clear TA and DONE bits, not requred as using default value
        @bic      r0, r0, #1<<SPI_CS_CSPOL @ Set CSPOL low, not requred as using default value
        bic      r0, r0, #1<<SPI_CS_REN @ Set REN low
        orr      r0, r0, #(SPI_CS_CPOL_HIGH<<SPI_CS_CPOL|SPI_CS_CPHA_BEGIN<<SPI_CS_CPHA) @ Set CPOL and CPHA as high
        @bic      r0, #0b11   @ Use ADC chip select 0, not requred as using default value
        swi      SPI_WriteSpiCs @ Writes from r0

7:      swi      SPI_ReadSpiCs @ Returns in r0
        orr      r0, r0, #(SPI_CS_CLEAR_BOTH<<SPI_CS_CLEAR|SPI_CS_TA_ACTIVE<<SPI_CS_TA) @#(SPI_CS_TA_ACTIVE<<SPI_CS_TA) @ Clear FIFO 
        swi      SPI_WriteSpiCs @ Writes from r0

1:      swi      SPI_ReadSpiCsTxd @ Returns TXD bit into r0 [0|1]
        cmp      r0, #0
        beq      1b

        mov      r0, #0x68
        swi      SPI_WriteSpiByte @ Writes lowest 8 bits of r0 to FIFO
        
2:      swi      SPI_ReadSpiCsTxd @ Returns TXD bit into r0 [0|1]
        cmp      r0, #0
        beq      2b

        mov      r0, #0x00
        swi      SPI_WriteSpiByte @ Writes lowest 8 bits of r0 into FIFO

3:      swi      SPI_ReadSpiCsDone @ Reads DONE bit into r0 [0|1]
        cmp      r0, #1
        bne      3b

4:      swi      SPI_ReadSpiByte @ Reads FIFO byte into r0
        ldr      r3, [ip, #pass]
        and      r1, r3, #1
        add      r3, r3, #1
        str      r3, [ip, #pass]
        cmp      r1, #0
        beq      5f

        @ odd byte read LSB
        str      r0, [ip, #adc_temp]
        b        6f
        
5:      @ even byte read MSB
        ldr      r2, [ip, #adc_temp]
        orr      r2, r2, r0, lsl #8
        str      r2, [ip, #adc_val]

6:      swi      SPI_ReadSpiCsRxd @ Reads RXD bit [0|1]
        cmp      r0, #0
        bne      4b

        ldr      r0, [ip, #adc_val]
        @lsr      r0, r0, #8 @ Remove comment to display top 2 bits
        swi      GPIO_SetByteDisplay @ Display lowest 8 bits of r0 on the LEDs

        swi      SPI_ReadSpiCs @ Returns in r0
        bic      r0, r0, #1<<SPI_CS_TA @ Clear the TA bit
        swi      SPI_WriteSpiCs @ Writes from r0
        b        7b

On the Gertboard all the LEDs are set to out, GP0->B12, GP1->B11, GP4->B8, GP14->B10, GP15->B9, GP17->B7, GP18->B6, GP21->B5, GP22->B4, GP23->B3, GP24->B2, GP25->B1 and the SPI pins bridged. LEDs 9-12 remain lit currently and the lsb is LED 8. Probably more logical to reverse the connections.

Return to “Bare metal, Assembly language”