(摘) EPS32之Ulp学习摘要

声明:内容源自网络,版权归原作者所有。若有侵权请在网页聊天中联系我

ULP(Ultra Low Power 超低功耗)协处理器可以在主处理器处于深度睡眠模式时,使用 ADC、温度传感器和外部 I2C 传感器执行测量操作。ULP 协处理器可以访问 RTC_SLOW_MEM 内存区域及 RTC_CNTL、RTC_IO、SARADC 等外设寄存器。ULP 协处理器使用 32 位固定宽度的指令,32 位内存寻址,配备 4 个 16 位通用寄存器。

官方的资料还是比较丰富的。

访问ULP程序变量

在ULP程序中定义的全局符号可以在主程序中使用。

          .global me_count  //初始化me_count变量
me_count: .long 0
          move r3, me_count
          ld r3, r3, 0                

在程序中引用 extern uint32_t ulp_me_count; //总是以ulp_开头来引用

在读取 ULP 写的变量时,主应用程序需要屏蔽高 16 位(ULP 程序在 RTC 内存中只能使用 32 位字的低 16 位,因为寄存器是 16 位。就是说ULP是通过RTC内存来进行数据交换。连ULP程序都是加载到RTC内存中)

printf(“Last measurement value: %d\n”, ulp_last_measurement & UINT16_MAX);

启动ULP程序

在menuconfig中必须启用“Enable Ultra Low Power Coprocessor"选项,以便为ULP预留内存。

extern const uint8_t bin_start[] asm("_binary_ulp_app_name_bin_start");
extern const uint8_t bin_end[]   asm("_binary_ulp_app_name_bin_end");

void start_ulp_program() {
    ESP_ERROR_CHECK( ulp_load_binary(
        0 /* load address, set to 0 when using default linker scripts */,
        bin_start,
        (bin_end - bin_start) / sizeof(uint32_t)) );
}

这里的ulp_app_name在配置文件CMakeLists.txt匹配的。多个ulp时就需要修改了。

ulp_load_binary 用于将ulp载入rtc内存中。

一旦上述程序加载到 RTC 内存后,应用程序即可启动此程序,并将入口点的地址传递给 ulp_run 函数:

ESP_ERROR_CHECK( ulp_run(&ulp_entry - RTC_SLOW_MEM) );

urlp_run参数只有一个:入口点。象上方语句中的ulp_entry即entry变量,是在ulp代码中指定的。

代码中需要申请为全局标签。例如示例的tsens.S中:

    ....
	.global entry
entry:
     .....

ULP程序流程

ULP由定时器启动,调用ulp_run则启动此定时器。

程序中,使用 ulp_set_wakeup_period 函数来设置 ULP 定时器周期值,例如:

ulp_set_wakeup_period(0, 3*1000*1000);  //设置0号寄存器(0-4)3秒唤醒

指令集

ULP协处理器具有4个16位通用寄存器,分别标记为R0,R1,R2,R3。 它还具有一个8位计数器寄存器(stage_cnt),可用于实现循环。

ULP协处理器可以访问RTC_SLOW_MEM存储区域的8k字节。 存储器以32位字为单位寻址。

所有指令均为32位。 跳转指令,ALU指令,外设寄存器和存储器访问指令在1个周期内执行。 根据外设的操作,与外设(TSENS,ADC,I2C)配合使用的指令需要可变的周期数。

指令语法不区分大小写。大写字母和小写字母可以随意使用和混合。对于注册名和指令名都是如此。

        .global array
array:  .long 0
        .long 0
        .long 0
        .long 0

        MOVE R1, array
        MOVE R2, 0x1234
        ST R2, R1, 0      // array[0]=R2
        ST R2, R1, 4      // array[1]=R2 4字节偏移量
        ADD R1, R1, 2     // 将地址增加2个字(8字节)
        ST R2, R1, 0      // array[2]=R2
ADD 加

R3+R2->R1

1:    ADD R1, R2, R3        //R1 = R2 + R3

2:    Add R1, R2, 0x1234    //R1 = R2 + 0x1234

3:    .set value1, 0x03     //constant value1=0x03
      Add R1, R2, value1    //R1 = R2 + value1

4:    .global label         //变量标签声明
      Add R1, R2, label     //R1 = R2 + label
      ...
      label: nop            //变量标号定义
SUB 减

R2-R3->R1

1:         SUB R1, R2, R3             //R1 = R2 - R3

2:         sub R1, R2, 0x1234         //R1 = R2 - 0x1234

3:         .set value1, 0x03          //constant value1=0x03
           SUB R1, R2, value1         //R1 = R2 - value1
4:         .global label              //declaration of variable label
           SUB R1, R2, label          //R1 = R2 - label
             ....
  label:   nop                        //definition of variable label
AND 与

R2 & R3 -> R1

1:        AND R1, R2, R3          //R1 = R2 & R3

2:        AND R1, R2, 0x1234      //R1 = R2 & 0x1234

3:        .set value1, 0x03       //constant value1=0x03
          AND R1, R2, value1      //R1 = R2 & value1

4:        .global label           //declaration of variable label
          AND R1, R2, label       //R1 = R2 & label
              ...
  label:  nop                     //definition of variable label
OR 或

R2 | R3 -> R1

1:       OR R1, R2, R3           //R1 = R2 | R3

2:       OR R1, R2, 0x1234       //R1 = R2 | 0x1234

3:       .set value1, 0x03       //constant value1=0x03
         OR R1, R2, value1       //R1 = R2 | value1

4:       .global label           //declaration of variable label
         OR R1, R2, label        //R1 = R2 |label
         ...
  label: nop                     //definition of variable label
LSH 逻辑左移

R2 « R3 -> R1

1:       LSH R1, R2, R3            //R1 = R2 << R3

2:       LSH R1, R2, 0x03          //R1 = R2 << 0x03

3:       .set value1, 0x03         //constant value1=0x03
         LSH R1, R2, value1        //R1 = R2 << value1

4:       .global label             //declaration of variable label
         LSH R1, R2, label         //R1 = R2 << label
         ...
  label:  nop                       //definition of variable label
RSH 逻辑右移

R2 » R3 -> R1

MOVE
1:        MOVE       R1, R2            //R1 = R2

2:        MOVE       R1, 0x03          //R1 = 0x03

3:        .set       value1, 0x03      //constant value1=0x03
          MOVE       R1, value1        //R1 = value1

4:        .global     label            //declaration of label
          MOVE        R1, label        //R1 = address_of(label) / 4
          ...
  label:  nop                          //definition of label
ST 将数据存储到内存
1:        ST  R1, R2, 0x12        //MEM[R2+0x12] = R1

2:        .data                   //数据部分定义
  Addr1:  .word     123           // 定义 Addr1为16位的值123
          .set      offs, 0x00    // 定义常数 offs
          .text                   //定义文本部分
          MOVE      R1, 1         // R1 = 1
          MOVE      R2, Addr1     // R2 = Addr1
          ST        R1, R2, offs  // MEM[R2 +  0] = R1
                                  // MEM[Addr1 + 0] will be 32'h600001
LD 从内存加载数据
1:        LD  R1, R2, 0x12            //R1 = MEM[R2+0x12]

2:        .data                       //数据部分定义
  Addr1:  .word     123               // 定义 Addr1为16位的值123
          .set      offs, 0x00        // 定义常数 offs
          .text                       //定义文本部分
          MOVE      R1, 1             // R1 = 1
          MOVE      R2, Addr1         // R2 = Addr1 / 4 (标签地址转换为字) ?
          LD        R1, R2, offs      // R1 = MEM[R2 +  0]
                                      // R1 will be 123

JUMP 跳转

1:        JUMP       R1            // 跳转到R1中的地址 (R1地址是32位字)

2:        JUMP       0x120, EQ     // 如果ALU结果为0,则跳转到地址0x120(以字节为单位)

3:        JUMP       label         // 跳转到label
          ...
  label:  nop                      // 定义label

4:        .global    label         // Declaration of global label

          MOVE       R1, label     // R1 = label (value loaded into R1 is in words)
          JUMP       R1            // Jump to label
          ...
  label:  nop                      // Definition of label
JUMPR 相对位置跳转 (基于R0的条件)

跳转条件

    EQ (equal) – jump if value in R0 == threshold

    LT (less than) – jump if value in R0 < threshold

    LE (less or equal) – jump if value in R0 <= threshold

    GT (greater than) – jump if value in R0 > threshold

    GE (greater or equal) – jump if value in R0 >= threshold
1:pos:    JUMPR       16, 20, GE   // Jump to address (position + 16 bytes) if value in R0 >= 20

2:        //通过R0寄存器值,实现循环
          MOVE        R0, 16       // load 16 into R0
  label:  SUB         R0, R0, 1    // R0--
          NOP                      // do something
          JUMPR       label, 1, GE // jump to label if R0 >= 1
JUMPS 跳转到一个相对地址 (基于阶段计数器条件)
1:pos:    JUMPS     16, 20, EQ     // Jump to (position + 16 bytes) if stage_cnt == 20

2:        // 使用阶段计数器实现循环
          STAGE_RST                  // set stage_cnt to 0
  label:  STAGE_INC  1               // stage_cnt++
          NOP                        // do something
          JUMPS       label, 16, LT  // jump to label if stage_cnt < 16
STAGE_RST 重置阶段计数器
STAGE_INC 加阶段计数寄存器
STAGE_DEC 减阶段计数寄存器
HALT 结束程序
WAKE 芯片唤醒

指令从ULP发送一个中断到RTC控制器

1: is_rdy_for_wakeup:                   // 读取 RTC_CNTL_RDY_FOR_WAKEUP 位
          READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP)
          AND r0, r0, 1
          JUMP is_rdy_for_wakeup, eq    // 循环直到被置位(高电平)
          WAKE                          // 唤醒
          REG_WR 0x006, 24, 24, 0       // 停止ULP定时器 (清除 RTC_CNTL_ULP_CP_SLP_TIMER_EN)
          HALT                          // 停止ULP程序
SLEEP 设置ULP周期唤醒的定时器

SLEEP 1 (0-4)

WAIT 等待(延时)

WAIT 10

TSENS 使用温度传感器进行测量

TSENS R1, 1000 // 测量温度传感器1000个循环,并将结果存储到R1

ADC 用ADC进行测量

ADC R1, 0, 1 // 使用ADC1 pad2测量值,并将结果存储到R1中

I2C_RD 从I2C从机读取单字节

I2C_RD 0x10, 7, 0, 0 // 读取从设备的子地址0x10的数据,设置到SENS_I2C_SLAVE_ADDR0

I2C_RD Sub_addr, High, Low, Slave_sel

Sub_addr 进行读取的I2C从站地址

High,Low 要读取的位范围

Slave_sel 要使用的I2C从设备地址的索引

I2C_WR 写一个字节到I2C从设备

I2C_WR 0x20, 0x33, 7, 0, 1 //使用在SENS_I2C_SLAVE_ADDR1中设置的地址将字节0x33写入从站的子地址0x20。

REG_RD 从外设寄存器读取

REG_RD 0x120, 7, 4 //load 4 bits: R0 = {12’b0, REG[0x120][7:4]}

REG_WR 写入外设寄存器

REG_WR 0x120, 7, 0, 0x10 //set 8 bits: REG[0x120][7:0] = 0x10

宏用于外设寄存器访问

soc/soc_ulp.h 文件中定义了一些宏

READ_RTC_REG(rtc_reg, low_bit, bit_width) 从RTC寄存器中读取最多16位到R0中

#include "soc/soc_ulp.h"
#include "soc/rtc_cntl_reg.h"

READ_RTC_REG(RTC_CNTL_TIME0_REG, 0, 16)  /* Read 16 lower bits of RTC_CNTL_TIME0_REG into R0 */

READ_RTC_FIELD(rtc_reg, field) 从RTC寄存器中读取最多16位到R0寄存器中。

#include "soc/soc_ulp.h"
#include "soc/sens_reg.h"

/* Read 8-bit SENS_TSENS_OUT field of SENS_SAR_SLAVE_ADDR3_REG into R0 */
READ_RTC_FIELD(SENS_SAR_SLAVE_ADDR3_REG, SENS_TSENS_OUT)

WRITE_RTC_REG(rtc_reg, low_bit, bit_width, value) 将立即值写入RTC寄存器

WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG, RTC_GPIO_OUT_DATA_W1TS_S + 2, 1, 1)

WRITE_RTC_FIELD(rtc_reg, field, value)

WRITE_RTC_FIELD(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN, 0)

相关文章