设为首页 加入收藏

TOP

SPI-SPI单线半双工数据收发应用笔记
2023-07-23 13:25:37 】 浏览:22
Tags:SPI-SPI 单线半 双工数 应用笔

SPI单线半双工数据收发应用笔记

SPI 接口可以工作在单线半双工模式,即主设备使用 MOSI 引脚,从设备使用 MISO 引脚进行通讯。CH32V203C8T6 芯片内置两路 SPI,使用 SPI1 作为主机,SPI2 作为从机,配合 DMA 完成 SPI 接口的单线半双工通信测试。

查阅应用手册 SPI 章节的寄存器描述,不难发现其关键在于通信过程中正确切换控制寄存器1中 BIDIOE 位。当 BIDIOE 置位时,主机处于发送状态,此时通过 DMA 将所需发送的数据搬运到数据寄存器中,即可完成发送过程。当 BIDIOE 复位时,主机处于接收状态,此时,主机仅通过时钟线持续输出既定频率的时钟信号。

 

1. SPI_InitTypeDef SPI_InitStructure = {0};

2.  

3. RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

4. RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);

5.  

6. // SPI1 HOST

7. SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx; // 单线半双工发送状态

8. SPI_InitStructure.SPI_Mode = SPI_Mode_Master; // 主机

9. SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;

10. SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;

11. SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;

12. SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // 软件片选

13. SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;

14. SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_LSB;

15. SPI_InitStructure.SPI_CRCPolynomial = 7;

16. SPI_Init(SPI1, &SPI_InitStructure);

17.  

18. // SPI2 SLAVE

19. SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Rx; // 单线半双工接收状态

20. SPI_InitStructure.SPI_Mode = SPI_Mode_Slave; // 从机

21. SPI_InitStructure.SPI_NSS = SPI_NSS_Hard; // 硬件片选

22. SPI_Init(SPI2, &SPI_InitStructure);

23.  

24. SPI_Cmd(SPI1, ENABLE);

25. SPI_Cmd(SPI2, ENABLE);

 

为了能够实现数据正常的接收,在初始化时,先将 SPI1 主机配置为单线发送状态,将 SPI2 从机配置为单线接收状态。

SPI 作为从机时,处于完全被动状态,在片选状态下,只要主机向外输出时钟信号,从机将持续的把数据寄存器中的数据向外移出。这种情况下需要特殊的处理,以保证 SPI 不会开始一次新的传输。为了简化这一操作,SPI2 配置时使用了硬件片选进行控制。

在初始化 DMA 时,应注意不使能 SPI1&2 的发送通道,避免 SPI 从机在未切换完成前进行发送操作。实际测试 DMA 接收通道,并发现存在接收异常的问题。

 

1. printf("SPI1 Tx...\r\n");

2. GPIO_ResetBits(GPIOA, GPIO_Pin_4);

3. DMA_Cmd(DMA1_Channel3, ENABLE); // 主机(SPI1)数据发送

4. while(DMA_GetFlagStatus(DMA1_FLAG_TC3) == 0); // 等待主机DMA(SPI1)操作完成

5. while(DMA_GetFlagStatus(DMA1_FLAG_TC4) == 0); // 等待从机DMA(SPI2)操作完成

6. GPIO_SetBits(GPIOA, GPIO_Pin_4); // 主机释放片选信号

7.  

8. printf("SPI2 Tx...\r\n");

9. SPI2->CTLR1 |= 1<<14; // 从机(SPI2)切换为发送状态

10. /* 先准备好第一个需要发送的数据,等待片选及时钟信号

11. * 避免出现在SPI时钟较高时,数据寄存器未完成更新的问题

12. * */

13. DMA_Cmd(DMA1_Channel5, ENABLE); // 从机(SPI2)数据发送

14. GPIO_ResetBits(GPIOA, GPIO_Pin_4); // 片选

15. SPI1->CTLR1 &= ~(1<<14); // 主机(SPI1)切换为接收状态

16.  

17. while(DMA_GetFlagStatus(DMA1_FLAG_TC5) == 0); // 等待从机DMA(SPI2)操作完成

18. // 等待SPI2发送完成

19. __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();

20. __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();

21. __NOP();__NOP();__NOP();__NOP();

22. GPIO_SetBits(GPIOA, GPIO_Pin_4);

23. while(DMA_GetFlagStatus(DMA1_FLAG_TC2) == 0); // 等待主机DMA(SPI1)操作完成

24. SPI1->CTLR1 |= 1<<14; // 主机(SPI1)切换为发送状态

25. SPI2->CTLR1 &= ~(1<<14); // 从机(SPI2)切换为接收状态

 

测试过程中,还需要注意 SPI2 作为从机发送数据的过程。应首先将 SPI 从机切换至发送状态,以留出充足的时间,给 DMA 将所需发送的数据搬运到数据寄存器中。

在测试时还使用了 NOP 函数,用于解决从机发送完成最后一包数据后,依然有数据输出的问题,通过在从机 DMA 将数据搬运完成后,添加一段延时,等待 SPI 从机将数据发送完成,此时主机主动释放片选信号,停止从机数据的发送。延时所需的时间与系统主频和 SPI 时钟频率有关,需在开发过程中根据实际情况进行调整。

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇STM32为何在诸多的单片机中脱颖而.. 下一篇Qt桌面应用程序打包

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目