20191027(32)RT-Thread SPI 设备挂载——ADS1256 后续提供具体实现源码(stm32f407)

目的

1 实现 ADS 1256 采集芯片在 RT-Thread 上的应用
2 了解 SPI 挂载的方式

特别说明

在中断 ISR 中调用 SPI 发送数据相关函数,导致 assertion 报错。Function[rt_mutex_take] shall not be used in ISR,assertion failed at function:rt_mutex_take, line number:656
RT-Thread SPI 和 I2C 数据收发相关函数会调用 rt_mutex_take(),此函数不能再中断函数中使用

官方外设精品文章链接

正文

了解 SPI

SPI:

1 串行外设接口总线(SPI)最早由Motorola首先提出的全双工三线同步串行外围接口(SCK, MISO 主入从出, MOSI 主出从入)
2 采用 主从模式 支持一对多
3 通过 CS 片选脚来确定工作对象(大多是 CS 片选脚都是拉低有效,在RTOS 中如果是拉高有效需要另外设置)

设置 SPI 参数(需要查看器件数据手册确定)

1 时钟速率
2 数据数据格式 (MSB 高位在前) / (LSB 低位在前)
3 时钟极性 CPOL 和 时钟相位 CPHA(CubeMX 中的 1边沿 / 2边沿就是对应数字 0 / 1

在这里插入图片描述


参考 SPI 参考原网址
CPOL=0,CPHA=0:此时空闲态时,SCLK处于低电平,数据采样是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在上升沿,数据发送是在下降沿。
CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据发送是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。
CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采集是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。
CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据发送是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿

在这里插入图片描述


了解 RTOS 下的 SPI 接口(关键 3 个)

1 挂载 SPI (默认在 SPI 总线会自动注册, 只需在 env 中集合 STM32CubeMX 配置即可
2 自定义消息
3 使用官方封住好的接口
4 总线使用和片选设定
5 配置

挂载 SPI

非 STM32 使用 rt_spi_bus_attach_device() 需求启动总线但是没有具体实验不再深入说明
STM32 使用 rt_hw_spi_device_attach()


 __HAL_RCC_GPIOB_CLK_ENABLE();  //对应 GPIOB 
 rt_hw_spi_device_attach("spi1", "spi10", GPIOB, GPIO_PIN_14); 
 // GPIOB  和 GPIO_PIN_14 是用来说明 CS 所在 IO 引脚 B14 脚
 //特别说明 GPIO_PIN_14 与 PIN 中引脚使用方式略微不同的
 //GPIO_PIN_0 ~ GPIO_PIN_15 
 //假设现在 CS =》 PA4 ----> GPIOA, GPIO_PIN_4 即可
 //"spi1" 总线名词
 //"spi10" 是指 spi1 下的第一个设备,具体请查看官方文档
 //"spi11" 是指 spi1 下的第二个设备
自定义消息

关键点:
1 第一个消息块必须使能(cs_take = 1) CS脚 和 最后一个消息块必须释放(cs_release = 1) CS 脚
2 8bit视为 1 个字节, 16bit 视为 2 个字节
3 假设有 5 个 8bit 数据接收, length = 5
4 不在一个消息块上同时收发,发和收需要分成 2 个或者使用 官方包装好的(官方自动会在使用前后使能和释放)

struct rt_spi_message msg1, msg2, msg3;
msg1.send_buf = &w25x_read_id;
msg1.recv_buf = RT_NULL; 
msg1.length = 1; 
msg1.cs_take = 1; 
msg1.cs_release = 0; 
msg1.next = &msg2; 

msg2.send_buf = &w25x_xxx; 
msg2.recv_buf = RT_NULL; 
msg2.length = 1; 
msg2.cs_take = 0; 
msg2.cs_release = 0; 
msg2.next = RT_NULL; 

msg3.send_buf = RT_NULL; 
msg3.recv_buf = id; 
msg3.length = 5; 
msg3.cs_take = 0; 
msg3.cs_release = 1; 
msg3.next = RT_NULL; 

rt_spi_transfer_message(spi_dev_w25q, &msg1);
官方封装库

只说明一个 rt_spi_send_then_recv
其特点就是 也是利用 上面的消息块分装的,但是使用前后会 自动使能和释放片选

其他

1 避免被不同线程打断 SPI 传输需要,先对 SPI 获取使用权 rt_spi_take_bus() / 使用完成之后释放 rt_spi_release_bus()
2 不同从设备使用同一 SPI 需要指定对应片选引脚有效 rt_spi_take() / 使用完成之后释放 rt_spi_release()
3 新增信息内容 rt_spi_transfer_message()

配置 SPI

在这里插入图片描述
在这里插入图片描述
特别说明:

1 具体 SPI 频率是根据自动选取 系统时钟 / 分频 满足要求的一个
(stm32f407 假设 1.92 最大 84M / 64 = 1.3125 M/s 这将是设定的实际频率)


ADS1256 部分例程
#define SPI_ADS1256_DEVICE_NAME "spi10"
#define SPI_CS0           GPIO_PIN_4 //PA4 CS0
#define SPI_CS0_GPIO  GPIOA 
#define SPI_SYNC0      GET_PIN(A, 8) //PA8 SYNC0
#define SPI_DRDY0      GET_PIN(C, 4) //PC4 DRDY0
#define SPI_RST0         GET_PIN(C, 5)  //PC5 RST0


static struct rt_spi_device *spi_dev_ads1256;

static void rt_hw_ads1256_gpio(void) {
	rt_pin_mode(SPI_SYNC0, PIN_MODE_OUTPUT);
	rt_pin_mode(SPI_RST0, PIN_MODE_OUTPUT);
	rt_pin_mode(SPI_CS0, PIN_MODE_OUTPUT);
	rt_pin_mode(SPI_DRDY0, PIN_MODE_INPUT);
	
	rt_pin_write(SPI_SYNC0, PIN_HIGH);
	rt_pin_write(SPI_RST0, PIN_HIGH);
	rt_pin_write(SPI_CS0, PIN_HIGH);
}

static int rt_hw_ads1256_init(void) 
{
	rt_err_t res;
    //挂载设备
    __HAL_RCC_GPIOA_CLK_ENABLE();
    res = rt_hw_spi_device_attach(SPI_BUS_NAME, SPI_ADS1256_DEVICE_NAME, 
    							SPI_CS0_GPIO, SPI_CS0); //挂载到 SPI 总线
    							
    if (RT_EOK != res) {
        return -RT_ERROR;
    }
    
    //查找设备
    spi_dev_ads1256 = (struct rt_spi_device *)rt_device_find(SPI_ADS1256_DEVICE_NAME);

    //配置设备
    {
        struct rt_spi_configuration cfg;
        cfg.data_width = 8;
        cfg.mode = RT_SPI_MASTER | RT_SPI_MSB | RT_SPI_MODE_1;
        cfg.max_hz = 1920 * 1000; //1.92 M

        rt_spi_configure(spi_dev_ads1256, &cfg);
    }
    //ADS IO 配置
    rt_hw_ads1256_gpio();// 非必须
    return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_ads1256_init); //系统自动注册

/*
@brief: 读取 ADS 1256 的设备 ID
@process: 当打印值为 0x30 时则说明硬件连接正常(具体可查看手册)
*/
static void rt_hw_ads1256_readChipID(void) {
    rt_uint8_t cmd[] = { 0x10, 0x00}; // 0x10 是读寄存器 0x00 读一个寄存器
    rt_uint8_t id = 0;
    
    //发送数据后接收数据
    rt_spi_send_then_recv(spi_dev_ads1256, cmd, 2, &id, 1); //发送 2 指令 接收 1 指令
    rt_kprintf(" read ADS1256 ID: %x \n", id);  // 接收到 0x30 表示硬件通讯正常
 
}
MSH_CMD_EXPORT(rt_hw_ads1256_readChipID, spi ads1256 sample);

发布了120 篇原创文章 · 获赞 27 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/qq_24890953/article/details/102835060