FuShenxiao 发表于 2024-8-17 11:17

小熊派BearPi-Pico H2821星闪开发板测评(四)——多种有线数据传输测试

<p><span style="font-size:18px;"><strong>UART</strong></span></p>

<p>UART为通用异步收发器,在实际接线中,一个设备的RX引脚接到另一个设备的TX引脚,除此之外,还需要定义串口号,波特率,数据位,起始位,停止位,以及数据传输的大小等。</p>

<p>小熊派上的UART代码实现:</p>

<pre>
<code>#include "pinctrl.h"
#include "uart.h"
#include "watchdog.h"
#include "osal_debug.h"
#include "cmsis_os2.h"
#include "app_init.h"

#define UART_BAUDRATE                      115200
#define UART_DATA_BITS                     3
#define UART_STOP_BITS                     1
#define UART_PARITY_BIT                  0
#define UART_TRANSFER_SIZE               512
#define CONFIG_UART_INT_WAIT_MS            5

#define UART_TASK_STACK_SIZE               0x1000
#define UART_TASK_DURATION_MS            500
#define UART_TASK_PRIO                     (osPriority_t)(17)

static uint8_t g_app_uart_rx_buff = { 0 };
#if defined(CONFIG_UART_INT_TRANSFER_MODE)
static uint8_t g_app_uart_int_rx_flag = 0;
#endif
static uart_buffer_config_t g_app_uart_buffer_config = {
    .rx_buffer = g_app_uart_rx_buff,
    .rx_buffer_size = UART_TRANSFER_SIZE
};


static void app_uart_init_pin(void)
{
    if (CONFIG_UART_BUS_ID == 0) {
      uapi_pin_set_mode(CONFIG_UART_TXD_PIN, HAL_PIO_UART_L0_TXD);
      uapi_pin_set_mode(CONFIG_UART_RXD_PIN, HAL_PIO_UART_L0_RXD);      
    }else if (CONFIG_UART_BUS_ID == 1) {
      uapi_pin_set_mode(CONFIG_UART_TXD_PIN, HAL_PIO_UART_H0_TXD);
      uapi_pin_set_mode(CONFIG_UART_RXD_PIN, HAL_PIO_UART_H0_RXD);      
    }else if (CONFIG_UART_BUS_ID == 2) {
      uapi_pin_set_mode(CONFIG_UART_TXD_PIN, HAL_PIO_UART_L1_TXD);
      uapi_pin_set_mode(CONFIG_UART_RXD_PIN, HAL_PIO_UART_L1_RXD);      
    }
}

static void app_uart_init_config(void)
{
    uart_attr_t attr = {
      .baud_rate = UART_BAUDRATE,
      .data_bits = UART_DATA_BITS,
      .stop_bits = UART_STOP_BITS,
      .parity = UART_PARITY_BIT
    };

    uart_pin_config_t pin_config = {
      .tx_pin = CONFIG_UART_TXD_PIN,
      .rx_pin = CONFIG_UART_RXD_PIN,
      .cts_pin = PIN_NONE,
      .rts_pin = PIN_NONE
    };
    uapi_uart_deinit(CONFIG_UART_BUS_ID);
    uapi_uart_init(CONFIG_UART_BUS_ID, &amp;pin_config, &amp;attr, NULL, &amp;g_app_uart_buffer_config);

}

#if defined(CONFIG_UART_INT_TRANSFER_MODE)
static void app_uart_read_int_handler(const void *buffer, uint16_t length, bool error)
{
    unused(error);
    if (buffer == NULL || length == 0) {
      osal_printk("uart%d int mode transfer illegal data!\r\n", CONFIG_UART_BUS_ID);
      return;
    }
    uint8_t *buff = (uint8_t *)buffer;
    if (memcpy_s(g_app_uart_rx_buff, length, buff, length) != EOK) {
      osal_printk("uart%d int mode data copy fail!\r\n", CONFIG_UART_BUS_ID);
      return;
    }
    g_app_uart_int_rx_flag = 1;
}

static void app_uart_write_int_handler(const void *buffer, uint32_t length, const void *params)
{
    unused(params);
    uint8_t *buff = (void *)buffer;
    for (uint32_t i = 0; i &lt; length; i++) {
      osal_printk("uart%d write data[%d] = %d\r\n", CONFIG_UART_BUS_ID, i, buff);
    }
   
}
#endif

static void *uart_task(const char *arg)
{
    unused(arg);
    /* UART pinmux. */
    app_uart_init_pin();

    /* UART init config. */
    app_uart_init_config();

#if defined(CONFIG_UART_INT_TRANSFER_MODE)
    osal_printk("uart%d int mode register receive callback start!\r\n", CONFIG_UART_BUS_ID);
    uapi_uart_unregister_rx_callback(CONFIG_UART_BUS_ID);
    if (uapi_uart_register_rx_callback(CONFIG_UART_BUS_ID, UART_RX_CONDITION_FULL_OR_IDLE,
                                       1, app_uart_read_int_handler) == ERRCODE_SUCC) {
      osal_printk("uart%d int mode register receive callback succ!\r\n", CONFIG_UART_BUS_ID);
    }
#endif

    while (1) {
      osDelay(UART_TASK_DURATION_MS);
#if defined(CONFIG_UART_POLL_TRANSFER_MODE)
      osal_printk("uart%d poll mode receive start!\r\n", CONFIG_UART_BUS_ID);
      (void)uapi_watchdog_kick();
      if (uapi_uart_read(CONFIG_UART_BUS_ID, g_app_uart_rx_buff, UART_TRANSFER_SIZE, 0) == UART_TRANSFER_SIZE) {
            osal_printk("uart%d poll mode receive succ!\r\n", CONFIG_UART_BUS_ID);
      }
      osal_printk("uart%d poll mode send back!\r\n", CONFIG_UART_BUS_ID);
      if (uapi_uart_write(CONFIG_UART_BUS_ID, g_app_uart_rx_buff, UART_TRANSFER_SIZE, 0) == UART_TRANSFER_SIZE) {
            osal_printk("uart%d poll mode send back succ!\r\n", CONFIG_UART_BUS_ID);
      }
#endif
#if defined(CONFIG_UART_INT_TRANSFER_MODE)
      while (g_app_uart_int_rx_flag != 1) {
            osDelay(CONFIG_UART_INT_WAIT_MS);
      }
      g_app_uart_int_rx_flag = 0;
      osal_printk("uart%d int mode send back!\r\n", CONFIG_UART_BUS_ID);
      if (uapi_uart_write_int(CONFIG_UART_BUS_ID, g_app_uart_rx_buff, UART_TRANSFER_SIZE, 0,
                              app_uart_write_int_handler) == ERRCODE_SUCC) {
            osal_printk("uart%d int mode send back succ!\r\n", CONFIG_UART_BUS_ID);
      }
#endif
    }

    return NULL;
}

static void uart_entry(void)
{
    osThreadAttr_t attr;

    attr.name = "UartTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = UART_TASK_STACK_SIZE;
    attr.priority = UART_TASK_PRIO;

    if (osThreadNew((osThreadFunc_t)uart_task, NULL, &amp;attr) == NULL) {
      /* Create task fail. */
    }
}

/* Run the uart_entry. */
app_run(uart_entry);
</code></pre>

<p>选择数据互传模式(CONFIG_UART_INT_TRANSFER_MODE),并用USB转TTL工具将开发板上定义的RX,TX引脚与PC相连,输入字符串,开发板会回复相应的ASCII码值。</p>

<p style="text-align: center;"> &nbsp;</p>

<p><strong>需要特别注意的是:</strong></p>

<ol>
        <li>H2821虽然有三个UART,但是UART0用于烧录和日志打印;UART1用于协议栈的日志打印,需要配套特殊工具才能看到;只有UART2是闲置的,因此在串口调试时,除去日志打印,最好使用UART2</li>
        <li>在设置数据位长度时,代码为&quot; #define UART_DATA_BITS 3 &quot;,在H2821的定义中,3位数据位即为8位数据位,同理,0-3位数据位即为5-8位数据位</li>
</ol>

<p><strong><span style="font-size:18px;">I2C主从数据传输测试</span></strong></p>

<p>I2C使用两根信号线进行通信:一根时钟线SCL,一根数据线SDA。IIC将SCL处于高时SDA拉低的动作作为开始信号,SCL处于高时SDA拉高的动作作为结束信号;传输数据时,SDA在SCL低电平时改变数据,在SCL高电平时保持数据,每个SCL脉冲的高电平传递1位数据。</p>

<p>小熊派上I2C代码实现:</p>

<p>对于主设备,需要定义SCL/SDA引脚、波特率、传输量以及主从设备地址:</p>

<pre>
<code>#include "pinctrl.h"
#include "i2c.h"
#include "osal_debug.h"
#include "cmsis_os2.h"
#include "app_init.h"

#define I2C_MASTER_ADDR                   0x0
#define I2C_SLAVE_ADDR                  0x8
#define I2C_SET_BAUDRATE                  500000


#define I2C_TASK_STACK_SIZE               0x1000
#define I2C_TASK_DURATION_MS            500
#define I2C_TASK_PRIO                     (osPriority_t)(17)

static void app_i2c_init_pin(void)
{
    /* I2C pinmux. */
    if (CONFIG_I2C_MASTER_BUS_ID == 0) {
      uapi_pin_set_mode(CONFIG_I2C_SCL_MASTER_PIN, HAL_PIO_I2C0_CLK);
      uapi_pin_set_mode(CONFIG_I2C_SDA_MASTER_PIN, HAL_PIO_I2C0_DATA);      
    }else if (CONFIG_I2C_MASTER_BUS_ID == 1) {
      uapi_pin_set_mode(CONFIG_I2C_SCL_MASTER_PIN, HAL_PIO_I2C1_CLK);
      uapi_pin_set_mode(CONFIG_I2C_SDA_MASTER_PIN, HAL_PIO_I2C1_DATA);      
    }
    uapi_pin_set_pull(CONFIG_I2C_SCL_MASTER_PIN, PIN_PULL_UP);
    uapi_pin_set_pull(CONFIG_I2C_SDA_MASTER_PIN, PIN_PULL_UP);
}

static void *i2c_master_task(const char *arg)
{
    unused(arg);
    i2c_data_t data = { 0 };

    uint32_t baudrate = I2C_SET_BAUDRATE;
    uint8_t hscode = I2C_MASTER_ADDR;
    uint16_t dev_addr = I2C_SLAVE_ADDR;

    /* I2C master init config. */

    app_i2c_init_pin();
    uapi_i2c_master_init(CONFIG_I2C_MASTER_BUS_ID, baudrate, hscode);

    /* I2C data config. */
    uint8_t tx_buff = { 0 };
    for (uint32_t loop = 0; loop &lt; CONFIG_I2C_TRANSFER_LEN; loop++) {
      tx_buff = (loop &amp; 0xFF);
    }

    uint8_t rx_buff = { 0 };
    data.send_buf = tx_buff;
    data.send_len = CONFIG_I2C_TRANSFER_LEN;
    data.receive_buf = rx_buff;
    data.receive_len = CONFIG_I2C_TRANSFER_LEN;

    while (1) {
      osDelay(I2C_TASK_DURATION_MS);
      osal_printk("i2c%d master send start!\r\n", CONFIG_I2C_MASTER_BUS_ID);
      if (uapi_i2c_master_write(CONFIG_I2C_MASTER_BUS_ID, dev_addr, &amp;data) == ERRCODE_SUCC) {
            osal_printk("i2c%d master send succ!\r\n", CONFIG_I2C_MASTER_BUS_ID);
      } else {
            continue;
      }
      osal_printk("i2c%d master receive start!\r\n", CONFIG_I2C_MASTER_BUS_ID);
      if (uapi_i2c_master_read(CONFIG_I2C_MASTER_BUS_ID, dev_addr, &amp;data) == ERRCODE_SUCC) {
            for (uint32_t i = 0; i &lt; data.receive_len; i++) {
                osal_printk("i2c%d master receive data is %x\r\n", CONFIG_I2C_MASTER_BUS_ID, data.receive_buf);
            }
            osal_printk("i2c%d master receive succ!\r\n", CONFIG_I2C_MASTER_BUS_ID);
      }
    }

    return NULL;
}

static void i2c_master_entry(void)
{
    osThreadAttr_t attr;

    attr.name = "I2cMasterTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = I2C_TASK_STACK_SIZE;
    attr.priority = I2C_TASK_PRIO;

    if (osThreadNew((osThreadFunc_t)i2c_master_task, NULL, &amp;attr) == NULL) {
      /* Create task fail. */
    }
}

/* Run the i2c_master_entry. */
app_run(i2c_master_entry);
</code></pre>

<p>对于从设备,只需定义SCL/SDA引脚和从设备地址即可:</p>

<pre>
<code>#include "pinctrl.h"
#include "i2c.h"
#include "errcode.h"
#include "osal_debug.h"
#include "cmsis_os2.h"
#include "app_init.h"

#define I2C_SLAVE_ADDR                  0x8
#define I2C_SET_BAUDRATE                  500000

#define I2C_TASK_STACK_SIZE               0x1000
#define I2C_TASK_DURATION_MS            100
#define I2C_TASK_PRIO                     (osPriority_t)(17)

static void app_i2c_init_pin(void)
{
    /* I2C pinmux. */
    if (CONFIG_I2C_SLAVE_BUS_ID == 0) {
      uapi_pin_set_mode(CONFIG_I2C_SCL_SLAVE_PIN, HAL_PIO_I2C0_CLK);
      uapi_pin_set_mode(CONFIG_I2C_SDA_SLAVE_PIN, HAL_PIO_I2C0_DATA);      
    }else if (CONFIG_I2C_SLAVE_BUS_ID == 1) {
      uapi_pin_set_mode(CONFIG_I2C_SCL_SLAVE_PIN, HAL_PIO_I2C1_CLK);
      uapi_pin_set_mode(CONFIG_I2C_SDA_SLAVE_PIN, HAL_PIO_I2C1_DATA);      
    }
    uapi_pin_set_pull(CONFIG_I2C_SCL_SLAVE_PIN, PIN_PULL_UP);
    uapi_pin_set_pull(CONFIG_I2C_SDA_SLAVE_PIN, PIN_PULL_UP);
}

void *i2c_slave_task(const char *arg)
{
    unused(arg);
    i2c_data_t data = { 0 };

    uint32_t baudrate = I2C_SET_BAUDRATE;
    uint16_t dev_addr = I2C_SLAVE_ADDR;

    /* I2C slave init config. */
    app_i2c_init_pin();
    uapi_i2c_slave_init(CONFIG_I2C_SLAVE_BUS_ID, baudrate, dev_addr);

    /* I2C data config. */
    uint8_t tx_buff = { 0 };
    for (uint32_t loop = 0; loop &lt; CONFIG_I2C_TRANSFER_LEN; loop++) {
      tx_buff = (loop &amp; 0xFF);
    }

    uint8_t rx_buff = { 0 };
    data.send_buf = tx_buff;
    data.send_len = CONFIG_I2C_TRANSFER_LEN;
    data.receive_buf = rx_buff;
    data.receive_len = CONFIG_I2C_TRANSFER_LEN;

    while (1) {
      osDelay(I2C_TASK_DURATION_MS);
      osal_printk("i2c%d slave receive start!\r\n", CONFIG_I2C_SLAVE_BUS_ID);
      if (uapi_i2c_slave_read(CONFIG_I2C_SLAVE_BUS_ID, &amp;data) == ERRCODE_SUCC) {
            for (uint32_t i = 0; i &lt; data.receive_len; i++) {
                osal_printk("i2c slave receive data is %x\r\n", data.receive_buf);
            }
            osal_printk("i2c%d slave receive succ!\r\n", CONFIG_I2C_SLAVE_BUS_ID);
      }
      osal_printk("i2c%d slave send start!\r\n", CONFIG_I2C_SLAVE_BUS_ID);
      if (uapi_i2c_slave_write(CONFIG_I2C_SLAVE_BUS_ID, &amp;data) == ERRCODE_SUCC) {
            osal_printk("i2c%d slave send succ!\r\n", CONFIG_I2C_SLAVE_BUS_ID);
      }
    }

    return NULL;
}

static void i2c_slave_entry(void)
{
    osThreadAttr_t attr;

    attr.name = "I2cSlaveTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = I2C_TASK_STACK_SIZE;
    attr.priority = I2C_TASK_PRIO;

    if (osThreadNew((osThreadFunc_t)i2c_slave_task, NULL, &amp;attr) == NULL) {
      /* Create task fail. */
    }
}

/* Run the i2c_slave_entry. */
app_run(i2c_slave_entry);
</code></pre>

<p>取两块核心板,将二者的SCL引脚相连、二者SDA引脚相连、二者GND相连,主设备发送数据,从设备接收数据,在串口工具中观察二者的收发日志信息。</p>

<p>输出结果如下:</p>

<p style="text-align: center;">&nbsp;</p>

<p><span style="font-size:18px;"><strong>I2S主从数据传输测试</strong></span></p>

<p>I2S主要应用于数字音频接口,用到的信号线包括时钟线、左/右声道线以及数据线。对于H2821,需要定义的引脚包括时钟引脚(CLK),通道选择引脚(WS),数据输出引脚(DO),数据输入引脚(DI)。这里的DO和DI我认为应该就是左右声道。</p>

<p>代码实现:</p>

<p>对于主设备,需要定义引脚、位宽、通道数、传输时间和数据量</p>

<pre>
<code>#include "common_def.h"
#include "pinctrl.h"
#include "i2s.h"
#include "tcxo.h"
#include "hal_sio.h"
#include "osal_debug.h"
#include "cmsis_os2.h"
#include "app_init.h"

#define I2S_DATA_WIDTH             24
#define I2S_NUMBER_OF_CHANNELS   2
#define TCXO_DELAY_MS            1000

#define I2S_TASK_STACK_SIZE      0x1000
#define I2S_TASK_DURATION_MS       500
#define I2S_TASK_PRIO            (osPriority_t)(17)

static uint32_t g_app_left_data;
static uint32_t g_app_right_data;
static i2s_tx_data_t g_app_write_data = {
    .left_buff = g_app_left_data,
    .right_buff = g_app_right_data,
    .length = CONFIG_I2S_TRANSFER_LEN,
};

static void app_i2s_init_pin(void)
{
    /* I2S pinmux. */
    uapi_pin_set_mode(CONFIG_I2S_CLK_MASTER_PIN, HAL_PIO_I2S_SCLK);
    uapi_pin_set_mode(CONFIG_I2S_WS_MASTER_PIN, HAL_PIO_I2S_WS);
    uapi_pin_set_mode(CONFIG_I2S_DO_MASTER_PIN, HAL_PIO_I2S_DOUT);
    uapi_pin_set_mode(CONFIG_I2S_DI_MASTER_PIN, HAL_PIO_I2S_DIN);
}

void app_i2s_rx_callbcak(uint32_t *left_buff, uint32_t *right_buff, uint32_t length)
{
    for (uint32_t i = 0; i &lt; length; i++) {
      osal_printk("left rx data is:0x%0x\r\n", left_buff);
      osal_printk("right rx data is:0x%0x\r\n", right_buff);
    }
}

static void *i2s_master_task(const char *arg)
{
    unused(arg);
    i2s_config_t config = {
      .drive_mode= MASTER,
      .transfer_mode = STD_MODE,
      .data_width = TWENTY_FOUR_BIT,
      .channels_num = TWO_CH,
      .timing = NONE_MODE,
      .clk_edge = NONE_EDGE,
      .div_number = I2S_DATA_WIDTH,
      .number_of_channels = I2S_NUMBER_OF_CHANNELS,
    };
    uint32_t i2s_first_data = 0x100000;
    for (uint32_t i = 0; i &lt; CONFIG_I2S_TRANSFER_LEN; i++) {
      g_app_left_data = i2s_first_data;
      g_app_right_data = i2s_first_data;
      i2s_first_data++;
    }
    app_i2s_init_pin();
    /* I2S init. */
    uapi_i2s_init(CONFIG_I2S_MASTER_BUS_ID, app_i2s_rx_callbcak);
    uapi_i2s_set_config(CONFIG_I2S_MASTER_BUS_ID, &amp;config);

    while (1) {
      osDelay(I2S_TASK_DURATION_MS);
      osal_printk("i2s master write start!\r\n");
      uapi_i2s_write_data(CONFIG_I2S_MASTER_BUS_ID, &amp;g_app_write_data);
      uapi_tcxo_delay_ms(TCXO_DELAY_MS);
    }

    return NULL;
}

static void i2s_master_entry(void)
{
    osThreadAttr_t attr;

    attr.name = "I2sMasterTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = I2S_TASK_STACK_SIZE;
    attr.priority = I2S_TASK_PRIO;

    if (osThreadNew((osThreadFunc_t)i2s_master_task, NULL, &amp;attr) == NULL) {
      /* Create task fail. */
    }
}

/* Run the i2s_master_entry. */
app_run(i2s_master_entry);
</code></pre>

<p>对于从设备,需要定义引脚、位宽和通道数:</p>

<pre>
<code>#include "common_def.h"
#include "pinctrl.h"
#include "i2s.h"
#include "hal_sio.h"
#include "osal_debug.h"
#include "cmsis_os2.h"
#include "app_init.h"

#define I2S_DATA_WIDTH             24
#define I2S_NUMBER_OF_CHANNELS   2

#define I2S_TASK_STACK_SIZE      0x1000
#define I2S_TASK_PRIO            (osPriority_t)(17)

static void app_i2s_init_pin(void)
{
    /* I2S pinmux. */
    uapi_pin_set_mode(CONFIG_I2S_CLK_SLAVE_PIN, HAL_PIO_I2S_SCLK);
    uapi_pin_set_mode(CONFIG_I2S_WS_SLAVE_PIN, HAL_PIO_I2S_WS);
    uapi_pin_set_mode(CONFIG_I2S_DO_SLAVE_PIN, HAL_PIO_I2S_DOUT);
    uapi_pin_set_mode(CONFIG_I2S_DI_SLAVE_PIN, HAL_PIO_I2S_DIN);
}

void app_i2s_rx_callbcak(uint32_t *left_buff, uint32_t *right_buff, uint32_t length)
{
    for (uint32_t i = 0; i &lt; length; i++) {
      osal_printk("left rx data is:0x%0x\r\n", left_buff);
      osal_printk("right rx data is:0x%0x\r\n", right_buff);
    }
}

static void *i2s_slave_task(const char *arg)
{
    unused(arg);
    i2s_config_t config = {
      .drive_mode= SLAVE,
      .transfer_mode = STD_MODE,
      .data_width = TWENTY_FOUR_BIT,
      .channels_num = TWO_CH,
      .timing = NONE_MODE,
      .clk_edge = NONE_EDGE,
      .div_number = I2S_DATA_WIDTH,
      .number_of_channels = I2S_NUMBER_OF_CHANNELS,
    };
    app_i2s_init_pin();
    /* I2S init. */
    uapi_i2s_init(CONFIG_I2S_SLAVE_BUS_ID, app_i2s_rx_callbcak);
    uapi_i2s_set_config(CONFIG_I2S_SLAVE_BUS_ID, &amp;config);

    osal_printk("i2s slave read start!\r\n");
    uapi_i2s_read_start(CONFIG_I2S_SLAVE_BUS_ID);

    return NULL;
}

static void i2s_slave_entry(void)
{
    osThreadAttr_t attr;

    attr.name = "I2sSlaveTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = I2S_TASK_STACK_SIZE;
    attr.priority = I2S_TASK_PRIO;

    if (osThreadNew((osThreadFunc_t)i2s_slave_task, NULL, &amp;attr) == NULL) {
      /* Create task fail. */
    }
}

/* Run the i2s_slave_entry. */
app_run(i2s_slave_entry);
</code></pre>

<p>取两块核心板,将二者的I2S CLK引脚相连,将二者的IS2 WS引脚相连,将一块的I2S&nbsp;DO引脚和另一块的I2S DI引脚相连,同理I2S DI和I2S DO相连,并将二者的GND相连。</p>

<p>输出结果如下:</p>

<p style="text-align: center;"> &nbsp;</p>

<p><strong><span style="font-size:18px;">SPI主从数据传输测试</span></strong></p>

<p>SPI为串行外围设备接口,是一种全双工的同步通信总线,信号线一般有四条:主设备输入/从设备输出(MISO),主设备输出/从设备输入(MOSI),串行时钟信号(SCLK),片选信号(CS)。对于H2821,需要定义时钟引脚CLK,使能引脚CS,输出引脚DO以及输入引脚DI。</p>

<p>代码实现:</p>

<p>对于主设备,需要定义引脚、从设备数量、时钟频率、时钟极性、时钟相位、框架格式、传输数据量等参数:</p>

<pre>
<code>#include "pinctrl.h"
#include "spi.h"
#include "osal_debug.h"
#include "cmsis_os2.h"
#include "app_init.h"

#define SPI_SLAVE_NUM                   1
#define SPI_FREQUENCY                   2
#define SPI_CLK_POLARITY                0
#define SPI_CLK_PHASE                   0
#define SPI_FRAME_FORMAT                0
#define SPI_FRAME_FORMAT_STANDARD       0
#define SPI_FRAME_SIZE_8                0x1f
#define SPI_TMOD                        0
#define SPI_WAIT_CYCLES               0x10

#define SPI_TASK_STACK_SIZE             0x1000
#define SPI_TASK_DURATION_MS            1000
#define SPI_TASK_PRIO                   (osPriority_t)(17)

static void app_spi_init_pin(void)
{
    if (CONFIG_SPI_MASTER_BUS_ID == 0) {
      uapi_pin_set_mode(CONFIG_SPI_DI_MASTER_PIN, HAL_PIO_SPI0_RXD);
      uapi_pin_set_mode(CONFIG_SPI_DO_MASTER_PIN, HAL_PIO_SPI0_TXD);
      uapi_pin_set_mode(CONFIG_SPI_CLK_MASTER_PIN, HAL_PIO_SPI0_SCLK);
      uapi_pin_set_mode(CONFIG_SPI_CS_MASTER_PIN, HAL_PIO_SPI0_CS0);      
    }else if (CONFIG_SPI_MASTER_BUS_ID == 1) {
      uapi_pin_set_mode(CONFIG_SPI_DI_MASTER_PIN, HAL_PIO_SPI1_RXD);
      uapi_pin_set_mode(CONFIG_SPI_DO_MASTER_PIN, HAL_PIO_SPI1_TXD);
      uapi_pin_set_mode(CONFIG_SPI_CLK_MASTER_PIN, HAL_PIO_SPI1_CLK);
      uapi_pin_set_mode(CONFIG_SPI_CS_MASTER_PIN, HAL_PIO_SPI1_CS0);   
    }else if (CONFIG_SPI_MASTER_BUS_ID == 2) {
      uapi_pin_set_mode(CONFIG_SPI_DI_MASTER_PIN, HAL_PIO_SPI2_RXD);
      uapi_pin_set_mode(CONFIG_SPI_DO_MASTER_PIN, HAL_PIO_SPI2_TXD);
      uapi_pin_set_mode(CONFIG_SPI_CLK_MASTER_PIN, HAL_PIO_SPI2_CLK);
      uapi_pin_set_mode(CONFIG_SPI_CS_MASTER_PIN, HAL_PIO_SPI2_CS0);         
    }
}

static void app_spi_master_init_config(void)
{
    spi_attr_t config = { 0 };
    spi_extra_attr_t ext_config = { 0 };

    config.is_slave = false;
    config.slave_num = SPI_SLAVE_NUM;
    config.bus_clk = SPI_CLK_FREQ;
    config.freq_mhz = SPI_FREQUENCY;
    config.clk_polarity = SPI_CLK_POLARITY;
    config.clk_phase = SPI_CLK_PHASE;
    config.frame_format = SPI_FRAME_FORMAT;
    config.spi_frame_format = HAL_SPI_FRAME_FORMAT_STANDARD;
    config.frame_size = SPI_FRAME_SIZE_8;
    config.tmod = SPI_TMOD;
    config.sste = 1;

    ext_config.qspi_param.wait_cycles = SPI_WAIT_CYCLES;
    uapi_spi_init(CONFIG_SPI_MASTER_BUS_ID, &amp;config, &amp;ext_config);
}

static void *spi_master_task(const char *arg)
{
    unused(arg);
    /* SPI pinmux. */
    app_spi_init_pin();

    /* SPI master init config. */
    app_spi_master_init_config();

    /* SPI data config. */
    uint8_t tx_data = { 0 };
    for (uint32_t loop = 0; loop &lt; CONFIG_SPI_TRANSFER_LEN; loop++) {
      tx_data = (loop &amp; 0xFF);
    }
    uint8_t rx_data = { 0 };
    spi_xfer_data_t data = {
      .tx_buff = tx_data,
      .tx_bytes = CONFIG_SPI_TRANSFER_LEN,
      .rx_buff = rx_data,
      .rx_bytes = CONFIG_SPI_TRANSFER_LEN,
    };

    while (1) {
      osDelay(SPI_TASK_DURATION_MS);
      osal_printk("spi%d master send start!\r\n", CONFIG_SPI_MASTER_BUS_ID);
      if (uapi_spi_master_write(CONFIG_SPI_MASTER_BUS_ID, &amp;data, 0xFFFFFFFF) == ERRCODE_SUCC) {
            osal_printk("spi%d master send succ!\r\n", CONFIG_SPI_MASTER_BUS_ID);
      } else {
            continue;
      }
      osal_printk("spi%d master receive start!\r\n", CONFIG_SPI_MASTER_BUS_ID);
      if (uapi_spi_master_read(CONFIG_SPI_MASTER_BUS_ID, &amp;data, 0xFFFFFFFF) == ERRCODE_SUCC) {
            for (uint32_t i = 0; i &lt; data.rx_bytes; i++) {
                osal_printk("spi%d master receive data is %x\r\n", CONFIG_SPI_MASTER_BUS_ID, data.rx_buff);
            }
            osal_printk("spi%d master receive succ!\r\n", CONFIG_SPI_MASTER_BUS_ID);
      }
    }

    return NULL;
}

static void spi_master_entry(void)
{
    osThreadAttr_t attr;

    attr.name = "SpiMasterTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = SPI_TASK_STACK_SIZE;
    attr.priority = SPI_TASK_PRIO;

    if (osThreadNew((osThreadFunc_t)spi_master_task, NULL, &amp;attr) == NULL) {
      /* Create task fail. */
    }
}

/* Run the spi_master_entry. */
app_run(spi_master_entry);
</code></pre>

<p>对于从设备,同样需要定义引脚、从设备数量、时钟频率、时钟极性、时钟相位、框架格式、传输数据量等参数:</p>

<pre>
<code>#include "pinctrl.h"
#include "spi.h"
#include "osal_debug.h"
#include "cmsis_os2.h"
#include "app_init.h"

#define SPI_SLAVE_NUM                   1
#define SPI_FREQUENCY                   2
#define SPI_CLK_POLARITY                0
#define SPI_CLK_PHASE                   0
#define SPI_FRAME_FORMAT                0
#define SPI_FRAME_FORMAT_STANDARD       0
#define SPI_FRAME_SIZE_8                0x1f
#define SPI_TMOD                        0
#define SPI_WAIT_CYCLES               0x10

#define SPI_TASK_STACK_SIZE             0x1000
#define SPI_TASK_DURATION_MS            1000
#define SPI_TASK_PRIO                   (osPriority_t)(17)

static void app_spi_init_pin(void)
{

    if (CONFIG_SPI_SLAVE_BUS_ID == 0) {
      uapi_pin_set_mode(CONFIG_SPI_DI_SLAVE_PIN, HAL_PIO_SPI0_RXD);
      uapi_pin_set_mode(CONFIG_SPI_DO_SLAVE_PIN, HAL_PIO_SPI0_TXD);
      uapi_pin_set_mode(CONFIG_SPI_CLK_SLAVE_PIN, HAL_PIO_SPI0_SCLK);
      uapi_pin_set_mode(CONFIG_SPI_CS_SLAVE_PIN, HAL_PIO_SPI0_CS0);      
    }else if (CONFIG_SPI_SLAVE_BUS_ID == 1) {
      uapi_pin_set_mode(CONFIG_SPI_DI_SLAVE_PIN, HAL_PIO_SPI1_RXD);
      uapi_pin_set_mode(CONFIG_SPI_DO_SLAVE_PIN, HAL_PIO_SPI1_TXD);
      uapi_pin_set_mode(CONFIG_SPI_CLK_SLAVE_PIN, HAL_PIO_SPI1_CLK);
      uapi_pin_set_mode(CONFIG_SPI_CS_SLAVE_PIN, HAL_PIO_SPI1_CS0);   
    }else if (CONFIG_SPI_SLAVE_BUS_ID == 2) {
      uapi_pin_set_mode(CONFIG_SPI_DI_SLAVE_PIN, HAL_PIO_SPI2_RXD);
      uapi_pin_set_mode(CONFIG_SPI_DO_SLAVE_PIN, HAL_PIO_SPI2_TXD);
      uapi_pin_set_mode(CONFIG_SPI_CLK_SLAVE_PIN, HAL_PIO_SPI2_CLK);
      uapi_pin_set_mode(CONFIG_SPI_CS_SLAVE_PIN, HAL_PIO_SPI2_CS0);         
    }
}


static void app_spi_slave_init_config(void)
{
    spi_attr_t config = { 0 };
    spi_extra_attr_t ext_config = { 0 };

    config.is_slave = true;
    config.slave_num = SPI_SLAVE_NUM;
    config.bus_clk = SPI_CLK_FREQ;
    config.freq_mhz = SPI_FREQUENCY;
    config.clk_polarity = SPI_CLK_POLARITY;
    config.clk_phase = SPI_CLK_PHASE;
    config.frame_format = SPI_FRAME_FORMAT;
    config.spi_frame_format = HAL_SPI_FRAME_FORMAT_STANDARD;
    config.frame_size = SPI_FRAME_SIZE_8;
    config.tmod = SPI_TMOD;
    config.sste = 1;

    ext_config.qspi_param.wait_cycles = SPI_WAIT_CYCLES;

#if defined(CONFIG_SPI_SUPPORT_DMA) &amp;&amp; (CONFIG_SPI_SUPPORT_DMA == 1)
    uapi_dma_init();
    uapi_dma_open();
#endif/* CONFIG_SPI_SUPPORT_DMA */

    uapi_spi_init(CONFIG_SPI_SLAVE_BUS_ID, &amp;config, &amp;ext_config);
}

static void *spi_slave_task(const char *arg)
{
    unused(arg);
    /* SPI pinmux. */
    app_spi_init_pin();

    /* SPI slave init config. */
    app_spi_slave_init_config();

    /* SPI data config. */
    uint8_t tx_data = { 0 };
    for (uint32_t loop = 0; loop &lt; CONFIG_SPI_TRANSFER_LEN; loop++) {
      tx_data = (loop &amp; 0xFF);
    }
    uint8_t rx_data = { 0 };
    spi_xfer_data_t data = {
      .tx_buff = tx_data,
      .tx_bytes = CONFIG_SPI_TRANSFER_LEN,
      .rx_buff = rx_data,
      .rx_bytes = CONFIG_SPI_TRANSFER_LEN,
    };

    while (1) {
      osDelay(SPI_TASK_DURATION_MS);
      osal_printk("spi%d slave receive start!\r\n", CONFIG_SPI_SLAVE_BUS_ID);
      if (uapi_spi_slave_read(CONFIG_SPI_SLAVE_BUS_ID, &amp;data, 0xFFFFFFFF) == ERRCODE_SUCC) {
            for (uint32_t i = 0; i &lt; data.rx_bytes; i++) {
                osal_printk("spi%d slave receive data is %x\r\n", CONFIG_SPI_SLAVE_BUS_ID, data.rx_buff);
            }
            osal_printk("spi%d slave receive succ!\r\n", CONFIG_SPI_SLAVE_BUS_ID);
      } else {
            continue;
      }
      osal_printk("spi%d slave send start!\r\n", CONFIG_SPI_SLAVE_BUS_ID);
      if (uapi_spi_slave_write(CONFIG_SPI_SLAVE_BUS_ID, &amp;data, 0xFFFFFFFF) == ERRCODE_SUCC) {
            osal_printk("spi%d slave send succ!\r\n", CONFIG_SPI_SLAVE_BUS_ID);
      }
    }

    return NULL;
}

static void spi_slave_entry(void)
{
    osThreadAttr_t attr;

    attr.name = "SpiSlaveTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = SPI_TASK_STACK_SIZE;
    attr.priority = SPI_TASK_PRIO;

    if (osThreadNew((osThreadFunc_t)spi_slave_task, NULL, &amp;attr) == NULL) {
      /* Create task fail. */
    }
}

/* Run the spi_slave_entry. */
app_run(spi_slave_entry);
</code></pre>

<p>取两块核心板,将二者的SPI CLK引脚相连,将二者的SPI CS引脚相连,将一块的SPI DO引脚与另一块的SPI DI引脚相连,同理SPI DI与SPI DO相连,将二者的GND相连。</p>

<p>输出结果如下:</p>

<p style="text-align: center;"> &nbsp;</p>

wangerxian 发表于 2024-8-19 17:52

<p>这么说只有一个串口能用,确实有点少。</p>

FuShenxiao 发表于 2024-8-19 20:38

wangerxian 发表于 2024-8-19 17:52
这么说只有一个串口能用,确实有点少。

<p>确实少了点,但是咱们不是冲它无线蓝牙去的嘛<img height="28" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/grinning-face-with-smiling-eyes_1f601.png" width="28" /></p>

wangerxian 发表于 2024-8-20 09:04

FuShenxiao 发表于 2024-8-19 20:38
确实少了点,但是咱们不是冲它无线蓝牙去的嘛

<p>倒也是,可以测试测试星闪功能~</p>

lvxinsjtu 发表于 2024-8-21 14:20

这个可以连星闪吗,功率够不够
页: [1]
查看完整版本: 小熊派BearPi-Pico H2821星闪开发板测评(四)——多种有线数据传输测试