Page 1 of 1

SPI bit-banging with ULP Programming

Posted: Thu Oct 25, 2018 10:09 am
by Prasad
Hi,

I'm trying to modify this example given in Espressif Github repository to work with MAX6675 which was originally developed to work with MS5611. Unfortunately i'm having trouble reading data with assembly. For the sake of simplicity i've disconnected the thermocouple to force the open circuit so it will return constant value from MAX6675 which gives me 0111 1111 1111 1111 1101 repeatedly,But i'm having trouble reading this value from the registry or storing to the registry. Instead I'm getting 00000000,00000000,10110111,11110110. Here i have attached my modified source code of spi.S, ms5611.S and main.c. Am i missing anything?

Repo - https://github.com/espressif/esp-iot-so ... es/ulp_spi

main.c

Code: Select all

/* ULP Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#include <stdio.h>
#include <math.h>
#include <unistd.h>
#include "esp_sleep.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/rtc_io_reg.h"
#include "soc/sens_reg.h"
#include "soc/soc.h"
#include "driver/rtc_io.h"
#include "esp32/ulp.h"
#include "sdkconfig.h"
#include "ulp_main.h"
#include "math.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

/* --- PRINTF_BYTE_TO_BINARY macro's --- */
#define PRINTF_BINARY_SEPARATOR ","
#define PRINTF_BINARY_PATTERN_INT8 "%c%c%c%c%c%c%c%c"
#define PRINTF_BYTE_TO_BINARY_INT8(i)    \
(((i) & 0x80ll) ? '1' : '0'), \
(((i) & 0x40ll) ? '1' : '0'), \
(((i) & 0x20ll) ? '1' : '0'), \
(((i) & 0x10ll) ? '1' : '0'), \
(((i) & 0x08ll) ? '1' : '0'), \
(((i) & 0x04ll) ? '1' : '0'), \
(((i) & 0x02ll) ? '1' : '0'), \
(((i) & 0x01ll) ? '1' : '0')

#define PRINTF_BINARY_PATTERN_INT16 \
PRINTF_BINARY_PATTERN_INT8               PRINTF_BINARY_SEPARATOR              PRINTF_BINARY_PATTERN_INT8
#define PRINTF_BYTE_TO_BINARY_INT16(i) \
PRINTF_BYTE_TO_BINARY_INT8((i) >> 8),   PRINTF_BYTE_TO_BINARY_INT8(i)
#define PRINTF_BINARY_PATTERN_INT32 \
PRINTF_BINARY_PATTERN_INT16              PRINTF_BINARY_SEPARATOR              PRINTF_BINARY_PATTERN_INT16
#define PRINTF_BYTE_TO_BINARY_INT32(i) \
PRINTF_BYTE_TO_BINARY_INT16((i) >> 16), PRINTF_BYTE_TO_BINARY_INT16(i)
#define PRINTF_BINARY_PATTERN_INT64    \
PRINTF_BINARY_PATTERN_INT32              PRINTF_BINARY_SEPARATOR              PRINTF_BINARY_PATTERN_INT32
#define PRINTF_BYTE_TO_BINARY_INT64(i) \
PRINTF_BYTE_TO_BINARY_INT32((i) >> 32), PRINTF_BYTE_TO_BINARY_INT32(i)
/* --- end macros --- */


uint32_t D2 = 0x00;             /* Digital temperature value */

extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start");
extern const uint8_t ulp_main_bin_end[]   asm("_binary_ulp_main_bin_end");

const gpio_num_t GPIO_CS = GPIO_NUM_25;
const gpio_num_t GPIO_MOSI = GPIO_NUM_26;
const gpio_num_t GPIO_SCLK = GPIO_NUM_27;
const gpio_num_t GPIO_MISO = GPIO_NUM_4;

static void init_ulp_program()
{
    esp_err_t err = ulp_load_binary(0, ulp_main_bin_start,(ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t));         
    ESP_ERROR_CHECK(err);

    rtc_gpio_init(GPIO_CS);
    rtc_gpio_set_direction(GPIO_CS, RTC_GPIO_MODE_OUTPUT_ONLY);
    rtc_gpio_set_level(GPIO_CS, 0);

    rtc_gpio_init(GPIO_MOSI);
    rtc_gpio_set_direction(GPIO_MOSI, RTC_GPIO_MODE_OUTPUT_ONLY);
    rtc_gpio_set_level(GPIO_MOSI, 0);

    rtc_gpio_init(GPIO_SCLK);
    rtc_gpio_set_direction(GPIO_SCLK, RTC_GPIO_MODE_OUTPUT_ONLY);
    rtc_gpio_set_level(GPIO_SCLK, 0);

    rtc_gpio_init(GPIO_MISO);
    rtc_gpio_set_direction(GPIO_MISO, RTC_GPIO_MODE_INPUT_ONLY);
    rtc_gpio_pullup_en(GPIO_MISO);

    /* Set ULP wake up period to 2s */
    ulp_set_wakeup_period(0, 2000*1000);
}

void app_main()
{
    
    init_ulp_program();
    ulp_D2_H = (uint16_t)65535;
    
    while (1) {
        D2 = (ulp_D2_L & UINT16_MAX);
        
        printf("Binary "PRINTF_BINARY_PATTERN_INT32 "\n",PRINTF_BYTE_TO_BINARY_INT32(D2));
        
        /* Start the ULP program */
        ESP_ERROR_CHECK( ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t)));
        
        vTaskDelay(2000 / portTICK_PERIOD_MS);
        
    }
}

ms5611.S

Code: Select all

/* ULP Example: using ADC in deep sleep

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.

   This file contains assembly code which runs on the ULP.
*/

/* ULP assembly files are passed through C preprocessor first, so include directives
   and C macros may be used in these files 
 */


#include "soc/rtc_cntl_reg.h"
#include "soc/rtc_io_reg.h"
#include "soc/soc_ulp.h"
#include "stack.S"


	/* Define variables, which go into .bss section (zero-initialized data) */
	.bss
	.global stack
stack:
	.skip 100
	.global stackEnd
stackEnd:
	.long 0

#if 1
	.global D2_L 				/* Digital temperature value, low 16bit */
D2_L:
	.long 0

	.global D2_H 				/* Digital temperature value, high 16bit */
D2_H:
	.long 0

#endif

	/* Code goes into .text section */
	.text
	.global entry
entry:
	move r3, stackEnd
    psr
    jump MS5611_Convert_D2
	jump wake_up
	/* Get ULP back to sleep */
	.global exit
exit:
	halt
	.global wake_up
wake_up:
    jump exit
    //jump wake_up, eq
    /* Wake up the SoC and stop ULP program */
    wake
    /* Stop the wakeup timer so it does not restart ULP */
    WRITE_RTC_FIELD(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN, 0)
    halt


.global waitMs
waitMs:
	wait 8000
	sub r2,r2,1 				/* Wait for r2 milliseconds */
	jump doneWaitMs,eq
	jump waitMs
doneWaitMs:
	ret


	.global MS5611_Convert_D2
MS5611_Convert_D2:
    psr
    jump SPI_Init                 /* init bitbang rtc gpio */
    move r2, 1000                    /* wait 1000ms */
    psr
    jump waitMs
    psr
    jump CS_Enable                 /* enable cs bus */
    psr
    jump SPI_Read_Byte
    ld r2, r2, 0
    move r1, D2_L
    st r2, r1, 0                 
    psr
    jump CS_Disable             /* disbale CS */
    ret
spi.S - Here i have changed SPI MODE to 4 and Read write bits to 16 since MAX6675 returns 16bit value

Code: Select all

/* ULP Example: Read temperautre in deep sleep

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.

   This file contains assembly code which runs on the ULP.

*/

/* ULP assembly files are passed through C preprocessor first, so include directives
   and C macros may be used in these files 
 */

#include "soc/rtc_cntl_reg.h"
#include "soc/rtc_io_reg.h"
#include "soc/soc_ulp.h"
#include "stack.S"

#define SPI_BIT16 1

#ifdef SPI_BIT16 				/* if define SPI_BIT16 write and read 16bit */
	.set bit_mask, 	0x8000
	.set bit_len, 	0x10
#else 							/* default spi write and read 8bit */
	.set bit_mask, 	0x80
	.set bit_len, 	0x08
#endif

	.set SPI_MODE_1,	1		/* Mode_1, Clock Polarity is 0 and Clock Phase is 0 */
	.set SPI_MODE_2,	2		/* Mode_2, Clock Polarity is 0 and Clock Phase is 1 */
	.set SPI_MODE_3,	3		/* Mode_3, Clock Polarity is 1 and Clock Phase is 0 */
	.set SPI_MODE_4,	4		/* Mode_4, Clock Polarity is 1 and Clock Phase is 1 */
	.set SPI_MASTER,	0		/* SPI Master */
	.set SPI_SLAVE,		1		/* SPI Slave */
	.set SPI_MODE_SET,	SPI_MODE_4
	.set SPI_TYPE_SET,	SPI_MASTER

.bss
	.global spi_mode
spi_mode:
	.long	0

	/* Code goes into .text section */
	.text

.macro spi_delay
	wait 10
.endm

.macro read_MISO 
	READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + 10, 1) 
.endm

.macro clear_SCLK
	WRITE_RTC_REG(RTC_GPIO_OUT_W1TC_REG, RTC_GPIO_OUT_DATA_W1TC_S + 17, 1, 1)
.endm
.macro set_SCLK
	WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG, RTC_GPIO_OUT_DATA_W1TS_S + 17, 1, 1)
.endm

.macro clear_MOSI
	WRITE_RTC_REG(RTC_GPIO_OUT_W1TC_REG, RTC_GPIO_OUT_DATA_W1TC_S + 7, 1, 1)
.endm
.macro set_MOSI
	WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG, RTC_GPIO_OUT_DATA_W1TS_S + 7, 1, 1)
.endm

.macro clear_CS 
	WRITE_RTC_REG(RTC_GPIO_OUT_W1TC_REG, RTC_GPIO_OUT_DATA_W1TC_S + 6, 1, 1)
.endm
.macro set_CS 
	WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG, RTC_GPIO_OUT_DATA_W1TS_S + 6, 1, 1)
.endm


	.global SPI_Init
SPI_Init:
	set_CS 						/* disable CS bus */
	move r1, SPI_MODE_SET
	sub r0, r1, SPI_MODE_1
	jump gpio_init1, eq 		/* init spi mode 1 gpio */
	sub r0, r1, SPI_MODE_2
	jump gpio_init2, eq 		/* init spi mode 2 gpio */
	sub r0, r1, SPI_MODE_3
	jump gpio_init3, eq 		/* init spi mode 3 gpio */
	sub r0, r1, SPI_MODE_4
	jump gpio_init4, eq 		/* init spi mode 4 gpio */
	jump error_loop 
gpio_init1:
	clear_MOSI
	clear_SCLK					/* */
	ret
gpio_init2:
	clear_MOSI
	clear_SCLK
	ret
gpio_init3:
	clear_MOSI
	set_SCLK
	ret
gpio_init4:
	clear_MOSI
	set_SCLK
	ret

	.global CS_Disable 			/* CS high level signal disable */
CS_Disable:
	set_CS
	ret

	.global CS_Enable 			/* CS low level signal enable */
CS_Enable:
	clear_CS
	ret

	.global	SPI_Write_Byte 		/* r2 save the data to be sent out */
SPI_Write_Byte:
	move r1, SPI_MODE_SET
	sub r0, r1, SPI_MODE_1
	jump write_mode_1, eq 		/* spi mode 1 */
	sub r0, r1, SPI_MODE_2
	jump write_mode_2, eq 		/* spi mode 2 */
	sub r0, r1, SPI_MODE_3
	jump write_mode_3, eq 		/* spi mode 3 */
	sub r0, r1, SPI_MODE_4
	jump write_mode_4, eq 		/* spi mode 4 */
	jump error_loop 			/* should be never get here */
write_mode_1:					/* Clock Polarity is 0 and Clock Phase is 0 */
	stage_rst
	clear_SCLK
write_loop1:
	clear_SCLK
	and r0, r2, bit_mask
	lsh r2, r2, 1
	jumpr loop1_bit0, 1, lt
	set_MOSI
	jump loop1_bit1
loop1_bit0:
	clear_MOSI
loop1_bit1:
	spi_delay
	set_SCLK
	spi_delay
	stage_inc 1
	jumps write_loop1, bit_len, lt
	clear_SCLK
	jump spi_write_byte_end
write_mode_2:					/* Clock Polarity is 0 and Clock Phase is 1 */
	clear_SCLK
	stage_rst
write_loop2:
	set_SCLK
	and r0, r2, bit_mask
	lsh r2, r2, 1
	jumpr loop2_bit0, 1, lt
	set_MOSI
	jump loop2_bit1
loop2_bit0:
	clear_MOSI
loop2_bit1:
	spi_delay
	clear_SCLK
	spi_delay
	stage_inc 1
	jumps write_loop2, bit_len, lt
	clear_SCLK
	jump spi_write_byte_end
write_mode_3: 					/* Clock Polarity is 1 and Clock Phase is 0 */
	set_SCLK
	stage_rst
write_loop3:
	set_SCLK
	and r0, r2, bit_mask
	lsh r2, r2, 1
	jumpr loop3_bit0, 1, lt
	set_MOSI
	jump loop3_bit1
loop3_bit0:
	clear_MOSI
loop3_bit1:
	spi_delay
	clear_SCLK
	spi_delay
	stage_inc 1
	jumps write_loop3, bit_len, lt
	set_SCLK
	jump spi_write_byte_end
write_mode_4: 					/* Clock Polarity is 1 and Clock Phase is 1 */
	set_SCLK
	stage_rst
write_loop4:
	clear_SCLK
	and r0, r2, bit_mask
	lsh r2, r2, 1
	jumpr loop4_bit0, 1, lt
	set_MOSI
	jump loop4_bit1
loop4_bit0:
	clear_MOSI
loop4_bit1:
	spi_delay
	set_SCLK
	spi_delay
	stage_inc 1
	jumps write_loop4, bit_len, lt
	set_SCLK
	jump spi_write_byte_end
spi_write_byte_end:
	clear_MOSI
	ret

	.global SPI_Burst_Write
SPI_Burst_Write:
	clear_CS
	spi_delay
	ret


/* spi read function */
	.global	SPI_Read_Byte
SPI_Read_Byte:
	move r1, SPI_MODE_SET
	sub r0, r1, SPI_MODE_1
	jump read_mode_1, eq 		/* spi mode 1 */
	sub r0, r1, SPI_MODE_2
	jump read_mode_2, eq 		/* spi mode 2 */
	sub r0, r1, SPI_MODE_3
	jump read_mode_3, eq 		/* spi mode 3 */
	sub r0, r1, SPI_MODE_4
	jump read_mode_4, eq 		/* spi mode 4 */
	jump error_loop
read_mode_1: 					/* Clock Polarity is 0 and Clock Phase is 0 */
	clear_SCLK
	stage_rst
read_loop1:
	clear_SCLK
	spi_delay
	set_SCLK
	read_MISO
	spi_delay
	lsh r2, r2, 1
	or r2, r2, r0
	stage_inc 1
	jumps read_loop1, bit_len, lt
	clear_SCLK
	jump spi_read_byte_end
read_mode_2:					/* Clock Polarity is 0 and Clock Phase is 1 */
	clear_SCLK
	stage_rst
read_loop2:
	set_SCLK
	spi_delay
	clear_SCLK
	read_MISO
	spi_delay
	lsh r2, r2, 1
	or r2, r2, r0
	stage_inc 1
	jumps read_loop2, bit_len, lt
	clear_SCLK
	jump spi_read_byte_end
read_mode_3: 					/* Clock Polarity is 1 and Clock Phase is 0 */
	set_SCLK
	stage_rst
read_loop3:
	set_SCLK
	spi_delay
	clear_SCLK
	read_MISO
	spi_delay
	lsh r2, r2, 1
	or r2, r2, r0
	stage_inc 1
	jumps read_loop3, bit_len, lt
	set_SCLK
	jump spi_read_byte_end
read_mode_4: 					/* Clock Polarity is 1 and Clock Phase is 1 */
	set_SCLK
	stage_rst
read_loop4:
	clear_SCLK
	spi_delay
	set_SCLK
	read_MISO
	spi_delay
	lsh r2, r2, 1
	or r2, r2, r0
	stage_inc 1
	jumps read_loop4, bit_len, lt
	set_SCLK
	jump spi_read_byte_end
spi_read_byte_end:
	ret

error_loop:
	ret
Any help would really appreciated.

Here is the logic output on my logic analyzer