SPI bit-banging with ULP Programming
Posted: Thu Oct 25, 2018 10:09 am
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
ms5611.S
spi.S - Here i have changed SPI MODE to 4 and Read write bits to 16 since MAX6675 returns 16bit value
Any help would really appreciated.
Here is the logic output on my logic analyzer
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);
}
}
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
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
Here is the logic output on my logic analyzer