目的
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);