RT-Thread SPI作为从模式接收数据的使用方法

最近遇到了如下需求:
mcu作为主控芯片通过spi与蓝牙芯片连接。
蓝牙芯片会时不时向mcu发送大量定长的数据包。
这种情况下,如果mcu的spi接口采用主模式,通过查询的方式询问蓝牙芯片是否有数据要发送,就会非常占用资源,并且遇到突发大量数据也可能会来不及处理。
比较好的一种方法是,mcu采用从模式的spi。蓝牙芯片无脑向mcu吐数据。如果主控mcu的spi时钟最大频率大于蓝牙芯片的spi最大频率,此方法可以跑到蓝牙芯片spi的传输极限。
大体思路:
初始化spi为从模式,并为spi_cs引脚注册中断函数,下降沿触发
在中断函数中,启动spi的接收。
spi接收完成后,做其他处理,比如解析,转发等
代码实现
下面是如何实现,平台采用了stm32f1系列芯片,启用spi dma传输,rt-thread 4.0.2,spi约定为slave,mode3,msb,cs active low。一次传输长度为package_length。
使用内存池+邮箱的缓冲方式,当然也可以使用消息队列,根据自己的喜好。此处对中断做了底半处理。
初始化
spi初始化
static struct rt_spi_device spi_device; //
static struct stm32_hw_spi_cs spi_cs; //中断引脚
static int spi_init(void)
{
rt_pin_mode(cs_pin, pin_mode_input_pullup);
/ attach the device to spi bus /
spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));
rt_assert(uwb_device != rt_null);
spi_cs = (struct stm32_hw_spi_cs )rt_malloc(sizeof(struct stm32_hw_spi_cs));
rt_assert(uwb_spi_cs != rt_null);
spi_cs->gpiox = gpioa;
spi_cs->gpio_pin = 4;
result = rt_spi_bus_attach_device(uwb_device, spi10, spi1, (void )uwb_spi_cs);
if (result != rt_eok)
{
log_e(%s attach to %s faild, %dn, spi10, spi1, result);
return result;
}
log_d(%s attach to %s done, uwb_spi_name, uwb_spi_bus);
/ get spi bus /
spi_device->bus->owner = spi_device;
/ configure spi device /
{
struct rt_spi_configuration cfg;
cfg.data_width = 8;
cfg.mode = rt_spi_slave | rt_spi_mode_3 | rt_spi_msb;
cfg.max_hz = 8 * 1000 * 1000;
rt_spi_configure(spi_device, &cfg);
}
if (rt_device_open((rt_device_t)spi_device, rt_device_flag_dma_rx) != rt_eok)
{
log_e(open uwb spi device %s error., spi10);
return -rt_error;
}
return rt_ok;
}
!!!注意,这里需要修改一下rt_spi_configure函数中的宏定义rt_spi_mode_mask,从
(rt_spi_cpha | rt_spi_cpol | rt_spi_msb)
改为
(rt_spi_slave | rt_spi_cpha | rt_spi_cpol | rt_spi_msb)
否则无法将spi接口配置为从模式,调用rt_spi_revice_message会崩溃。
初始化信号量,邮箱和内存池
/* create rx semaphore /
spi_start_sem = rt_sem_create(spi1_start, 0, rt_ipc_flag_fifo);
/ create rx mp /
spi_mp = rt_mp_create(spi_mp, spi_mb_len, rt_align(sizeof(rt_uint8_t), sizeof(intptr_t)) * package_length);
/ create rx mailbox /
rt_mb_init(&spi_mb, uwb_mb, &spi_mb_pool[0], sizeof(spi_mb_pool) / 4, rt_ipc_flag_fifo);
为cs引脚绑定中断函数
rt_pin_attach_irq(4, pin_irq_mode_falling, (void ( )(void *))spi_cs_isr, rt_null);
此时,可以先不使能中断,可以等待系统所有初始工作完成后,由其他线程使能中断,以启动spi接收。
到此,初始化的工作就完成了。接下来,要进行数据的接收工作,为此我们需要创建一些其他的函数。
数据的接收
首先我们需要创建一个中断函数,这个中断函数通过cs引脚的下降沿触发,用来通知系统开始接收数据。
static void spi_cs_isr(void)
{
/* enter interrupt /
rt_interrupt_enter();
rt_sem_release(spi_start_sem);
/ leave interrupt */
rt_interrupt_leave();
}
然后,我们还需要:一个线程用来启动dma接收;一个中断函数用于通知系统dma接收已经完成。
使用dma方式的好处是,全部的spi接收过程可以交给dma。这种非阻塞的方式使得,系统在这个时候可以搞搞其他事情(相当于双线程???)。在spi大量传输数据时尤其好用。
static uint8_t *rx_buff = rt_null;
static void spi_start_thread_entry(void *parameter)
{
struct rt_spi_message spi_msg;
spi_msg.send_buf = rt_null;
spi_msg.length = uwb_package_length;
spi_msg.cs_take = 0;
spi_msg.cs_take = 0;
spi_msg.next = rt_null;
while (1)
{
if (rt_sem_take(spi_start_sem, rt_waiting_forever) == rt_eok)
{
rx_buff = rt_mp_alloc(spi_mp, rt_waiting_no);
if (rx_buff != rt_null)
{
rt_spi_revice_message(spi_device, &spi_msg);
}
}
}
}
这里使用了rt_waiting_no的方式来申请空间,如果没有申请到(缓冲区已满),就抛弃这条数据。
使用rt_spi_revice_message函数来启动dma接收,并且约定了接收的长度固定为package_length。
dma接收完成函数
void hal_spi_rxcpltcallback(spi_handletypedef *hspi)
{
if (hspi->instance == spi1)
{
if (rx_buff != rt_null)
{
rt_mb_send(&spi_mb, (rt_uint32_t)rx_buff); //发送邮件
}
}
}
最后,还需要一个线程用于处理接收到的数据
static void spi_dma_clp_thread_entry(void *parameter)
{
rt_uint8_t *net_tx_buff = rt_null;
while (1)
{
if (rt_mb_recv(&uwb_mb, (rt_ubase_t *)&net_tx_buff, rt_waiting_forever) == rt_eok)
{
//todo
//data process
}
rt_mp_free(net_tx_buff);
net_tx_buff = rt_null;
}
}
到此,基于rt-thread的spi从接收就基本完成了。这些只是一个大体的思路,也可以使用自己喜欢的方式,或者添加其他的功能。如果大家有更好的思路,欢迎分享出来。

基于场景的嵌入式计算是国产MCU等芯片的突围之路吗?
使用配备新一代电容式触摸传感器的RX140 MCU改进UX/UI
抽屉式开关柜的安装步骤及方法
关于425 GHz波导带通滤波器的性能分析和应用
交换机的级连扩展与堆叠技术介绍
RT-Thread SPI作为从模式接收数据的使用方法
行业 | 雷曼光电COB技术产品良率已达95%
Arm在英国裁减约20%的员工
未来6G技术是什么样?其中的5大趋势与13个核心技术分析
远行必备,光积电户外电源重塑旅途体验
MEMS压力传感器校准的价值
5G不能完全取代光纤通信的原因
基于RFID的数据采集网络的设计与实现
三星galaxy系列升级android 4.0细节解读
频繁身陷反垄断官司 高通商业模式还能否持续?
PX2嵌入式芯片参数介绍
ST推出最低电流消耗电压比较器TS3011
xMEMS和Bujoen电子宣布立即推出用于2分频扬声器模块
用STM32测量频率和占空比的几种方法
利用AI和边缘计算盒子实现更安全、便捷的居住环境