HaaS EDU K1设备资源 之 SPI

1、概述

SPI, Serial Perripheral Interface, 串行外围设备接口, 是 Motorola 公司推出的一种同步串行接口技术. SPI 总线在物理上是通过接在外围设备微控制器(PICmicro) 上面的微处理控制单元 (MCU) 上叫作同步串行端口(Synchronous Serial Port) 的模块(Module)来实现的, 它允许 MCU 以全双工的同步串行方式, 与各种外围设备进行高速数据通信.

    SPI 主要应用在 EEPROM, Flash, 实时时钟(RTC), 数模转换器(ADC), 数字信号处理器(DSP) 以及数字信号解码器之间. 它在芯片中只占用四根管脚 (Pin) 用来控制以及数据传输, 节约了芯片的 pin 数目, 同时为 PCB 在布局上节省了空间. 正是出于这种简单易用的特性, 现在越来越多的芯片上都集成了 SPI技术。

 

CPOL与CPHA

CPOL与CPHA是SPI中一个非常重要的概念。其分别代表SPI的极性Polarity和相位Phase:
(1) CKPOL (Clock Polarity) = CPOL = POL = Polarity = (时钟)极性
(2) CKPHA (Clock Phase)   = CPHA = PHA = Phase = (时钟)相位

CPOL和CPHA,分别都可以是0或时1,对应的四种组合就是:

Mode 0

CPOL= 0,CPHA = 0

Mode 1

CPOL = 0,CPHA = 1

Mode 2

CPOL = 1,CPHA = 0

Mode 3

CPOL = 1,CPHA = 1

 

CPOL极性

          想了解CPOL之前,需要先了解什么是clock空闲时刻,字面理解就是此时CLK此时处于稳定状态,与此对应的,CLK在发送数据的时候,就是正常的工作的时候了。

SPI的CPOL,表示当SCLK空闲idle的时候,其电平的值是低电平0还是高电平1。如下图所示:

                                                     图1 CPOL示意图

CPOL=0,时钟空闲idle时候的电平是低电平,所以当SCLK有效的时候,就是高电平,这就是高有效;

CPOL=1,时钟空闲idle时候的电平是高电平,所以当SCLK有效的时候,就是低电平,这就是低有效;

 

CPHA相位

相位,对应着数据采样是在第几个边沿(edge),是第一个边沿还是第二个边沿,0对应着第一个边沿,1对应着第二个边沿。

CPHA=0,表示第一个边沿:
对于CPOL=0,idle时候的是低电平,第一个边沿就是从低变到高,所以是上升沿;
对于CPOL=1,idle时候的是高电平,第一个边沿就是从高变到低,所以是下降沿;
CPHA=1,表示第二个边沿:
对于CPOL=0,idle时候的是低电平,第二个边沿就是从高变到低,所以是下降沿;
对于CPOL=1,idle时候的是高电平,第一个边沿就是从低变到高,所以是上升沿;

                                            图 2 CPHA示意图

 

2、HAL层介绍

HaaS EDU K1上的HaaS1000有两路主模式的SPI,支持串行LCD,速率最高50MHz。分别接到OLED屏幕以及扩展接口。

对于不同底层驱动的SPI操作实现,统一封装成本文所述spi hal接口。 hal相关头文件位于目录:include/aos/hal。 hal相关实现位于具体的mcu目录下,如:platform/mcu/haas100/hal/。

2.1、API列表

hal_spi_init

初始化指定SPI端口

hal_spi_send

从指定的SPI端口发送数据

hal_spi_recv

从指定的SPI端口接收数据

hal_spi_send_recv

从指定的SPI端口发送并接收数据

hal_spi_finalize

关闭指定SPI端口

 

2.2、API详情

请参考include/aos/hal/spi.h

2.2.1、相关宏定义

#define HAL_SPI_MODE_MASTER 1 /* spi communication is master mode */

#define HAL_SPI_MODE_SLAVE  2 /* spi communication is slave mode */

 

2.2.2、相关结数据结构

spi_dev_t

typedef struct {

    uint8_t      port;   /* spi port */

    spi_config_t config; /* spi config */

    void        *priv;   /* priv data */

} spi_dev_t;

spi_config_t

typedef struct {

    uint32_t mode; /* spi communication mode */

    uint32_t freq; /* communication frequency Hz */

} spi_config_t;

 

2.2.3、hal_spi_init

初始化指定SPI端口

函数原型

int32_t hal_spi_init(spi_dev_t *spi)

参数

spi_dev_t *spi

入参

SPI设备描述,定义需要初始化的SPI参数

用户自定义一个spi_dev_t结构体

返回值

返回成功或失败, 返回0表示SPI初始化成功,非0表示失败

调用示例

spi0.port = 0;

spi0.config.data_size = SPI_DATA_SIZE_8BIT;

spi0.config.mode = SPI_WORK_MODE_3;

spi0.config.cs = 1;

spi0.config.freq = 2000000;

spi0.config.role = SPI_ROLE_MASTER;

spi0.config.firstbit = SPI_FIRSTBIT_MSB;

spi0.config.t_mode = SPI_TRANSFER_NORMAL;

 

2.2.4、hal_spi_send

从指定的SPI端口发送数据

函数原型

int32_t hal_spi_send(spi_dev_t *spi, const uint8_t *data, uint16_t size, uint32_t timeout)

参数

spi_dev_t *spi

入参

SPI设备描述

使用hal_spi_init初始化时传入值

const uint8_t *data

入参

指向要发送数据的数据指针

char spi_data_buf[10]

uint16_t size

入参

要发送的数据字节数

10

uint32_t timeout

入参

超时时间(单位ms),如果希望一直等待设置为HAL_WAIT_FOREVER

50

返回值

返回成功或失败, 返回0表示SPI数据发送成功,非0表示失败

调用示例

#define SPI_BUF_SIZE   10

#define SPI_TX_TIMEOUT 50

/* data buffer */

char spi_data_buf[SPI_BUF_SIZE];

ret = hal_spi_send(&spi1, spi_data_buf, SPI_BUF_SIZE, SPI_TX_TIMEOUT);


2.2.5、hal_spi_recv

从指定的SPI端口发送并接收数据

函数原型

int32_t hal_spi_recv(spi_dev_t *spi, uint8_t *data, uint16_t size, uint32_t timeout)

参数

spi_dev_t *spi

入参

SPI设备描述

使用hal_spi_init初始化时传入值

const uint8_t *data

入参

指向接收缓冲区的数据指针

char spi_data_buf[10]

uint16_t size

入参

期望接收的数据字节数

10

uint32_t timeout

入参

超时时间(单位ms),如果希望一直等待设置为HAL_WAIT_FOREVER

50

返回值

返回成功或失败, 返回0表示成功接收size个数据,非0表示失败

调用示例

#define SPI_BUF_SIZE   10

#define SPI_RX_TIMEOUT 50

/* data buffer */

char spi_data_buf[SPI_BUF_SIZE];

ret =hal_spi_recv(&spi1, spi_data_buf, SPI_BUF_SIZE, SPI_RX_TIMEOUT);

 

2.2.6、hal_spi_send_recv

从指定的SPI端口发送并接收数据

函数原型

int32_t hal_spi_send_recv(spi_dev_t *spi, uint8_t *tx_data, uint8_t *rx_data, uint16_t size, uint32_t timeout)

参数

spi_dev_t *spi

入参

SPI设备描述

使用hal_spi_init初始化时传入值

uint8_t *tx_data

入参

指向发送缓冲区的数据指针

char spi_send_buf[10]

uint8_t *rx_data

入参

指向接收缓冲区的数据指针

char spi_recv_buf[10]

uint16_t size

入参

要发送和接收数据字节数

10

uint32_t timeout

入参

超时时间(单位ms),如果希望一直等待设置为HAL_WAIT_FOREVER

50

返回值

返回成功或失败, 返回0表示成功发送和接收size个数据,非0表示失败

调用示例

#define SPI_BUF_SIZE   10

#define SPI_RX_TIMEOUT 50

/* data buffer */

char spi_send_buf[SPI_BUF_SIZE] = {0};

char spi_recv_buf[SPI_BUF_SIZE] = {0};

ret =hal_spi_send_recv(&spi1, spi_send_buf, spi_recv_buf,SPI_BUF_SIZE, SPI_RX_TIMEOUT);

 

2.2.7、hal_spi_finalize

关闭指定SPI端口

函数原型

int32_t hal_spi_finalize(spi_dev_t *spi)

参数

spi_dev_t *spi

入参

SPI设备描述

使用hal_spi_init初始化时传入值

返回值

类型:int 返回成功或失败, 返回0表示SPI关闭成功,非0表示失败。

调用示例

ret =hal_spi_finalize(&spi1);

 

3、案例介绍

EDU K1上共有两路SPI,一路默认连接了OLED屏幕,一路外接到30pin扩展口,本章以OLED为例,给大家介绍一下SPI的用法。

3.1、硬件实现

本文用到的硬件电路在开发板上默认是已经连接好了的。原理图如下:

               图3 SPI1部分接线图

 

3.2、软件实现

3.2.1、SPI初始化

HaaS EDU K1 中自带的OLED芯片是sh1106,这颗芯片用到了SPI1作为数据通道。

在对屏幕进行操作之前,先初始化SPI。

SPI1部分默认使用了26Mhz的频率,极性与相位均为1,采用的model3模式,CPOL=1,CPHA=1。
 

static inline int OLED_SPI_Init()

{
    spi_sh1106.port = 1;
    spi_sh1106.config.mode = SPI_WORK_MODE_3; // CPOL = 1; CPHA = 1
    spi_sh1106.config.freq = 26000000;
    spi_sh1106.config.role = SPI_ROLE_MASTER;
    spi_sh1106.config.firstbit = SPI_FIRSTBIT_MSB;
    spi_sh1106.config.t_mode = SPI_TRANSFER_NORMAL;
    spi_sh1106.config.serial_len = 8;
    spi_sh1106.config.data_size = SPI_DATA_SIZE_8BIT;
    spi_sh1106.config.cs = SPI_CS_DIS;

    hal_spi_init(&spi_sh1106);

    printf("hal_spi_init done\r\n");

    return 0;
}

 

3.2.2、SPI写操作

OLED由于特殊性,只支持SPI的写操作,具体OLED的可以参考。

OLED的写操作封装到了hal_spi_send接口中,

static inline void spi_write(const void *data, uint32_t size)

{
    if (size > SPI_MAX_BLOCK)

    {
        uint32_t rest = size;
        uint32_t start = 0;
        while (rest > SPI_MAX_BLOCK)
        {

            hal_spi_send(&spi_sh1106, (const void *)(data + start), SPI_MAX_BLOCK, 10);
            start += SPI_MAX_BLOCK;
            rest -= SPI_MAX_BLOCK;
        }
        hal_spi_send(&spi_sh1106, (const void *)(data + start), rest, 10);
    }
    else
    {
        hal_spi_send(&spi_sh1106, (const void *)data, (uint16_t)size, 10);
    }
}

 

3.3、编译与下载

3.3.1、代码准备

打开edu_demo的产测开关

application/example/edu_demo/Config.in

在该文件中修改编译选项,将default n改为y,从而打开EDK_DEMO_FACTORY_TEST_ENABLIE开关。

config EDK_DEMO_FACTORY_TEST_ENABLIE

    bool "enable factory test function"

    default y

加入Demo到启动代码

application/example/edu_demo/app_entry.c

函数application_start中注释掉menu_init();   添加如下两行,

        //menu_init();

        OLED_Show_String(12, 12, "spi1 test", 16, 1); 修改刷新缓冲区

        OLED_Refresh_GRAM(); //刷新缓冲区到屏幕上

 

3.3.2、编译

命令行方式进行编译

aos make distclean

aos make edu_demo@haaseduk1 -c config

aos make

 

3.3.3、烧录

命令行方式

aos upload

图形界面方式

详见HaaS EDU K1 快速开始 第4.3.3章节-使用GUI工具烧录部分。

 

开发者技术支持

如需更多技术支持,可加入钉钉开发者群,或者关注微信公众号

更多技术与解决方案介绍,请访问阿里云AIoT首页https://iot.aliyun.com/

猜你喜欢

转载自blog.csdn.net/HaaSTech/article/details/115130578