查看: 691|回复: 0
收起左侧

第一百零九章:CH32V103应用教程——SPI-单线双工互发互收

[复制链接]

  离线 

  • TA的每日心情
    奋斗
    2022-6-21 08:23
  • 签到天数: 2 天

    [LV.1]

    发表于 2021-11-30 01:24:10 | 显示全部楼层 |阅读模式

    有人预言,RISC-V或将是继Intel和Arm之后的第三大主流处理器体系。欢迎访问全球首家只专注于RISC-V单片机行业应用的中文网站

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x
    本帖最后由 塞巴斯蒂安 于 2021-11-30 01:23 编辑

    本章教程主要进行SPI单工通信方式下1条时钟线和1条双向数据线配置的应用,从机接收主机发送的数据,并将接收的数据返回给主机。

    1、SPI单工通信简介

    当配置SPI为1条时钟线和1条双向数据线时,设置SPI_CR1寄存器中的BIDIMODE位而启用此模式。在这个模式下,SCK引脚作为时钟,主设备使用MOSI引脚而从设备使用MISO引脚作为数据通信。传输的方向由SPI_CR1寄存器里的BIDIOE控制,当这个位是’1’的时候,数据线是输出,否则是输入。关于其具体应用,可见下述程序。

    关于CH32V103 SPI具体信息,可参考CH32V103应用手册。SPI标准库函数在第十五章节已介绍,在此不再赘述。

    2、硬件设计

    本章教程主要在SPI单工通信模式下选择1条时钟线和1条双向数据线进行主机接收从机发送实验,需用到两个开发板,且由于采用1条时钟线和1条双向数据线配置,因此主设备使用MOSI引脚,从设备使用MISO引脚进行通讯。此处使用外设为SPI1,主设备MOSI对应引脚为PA7引脚,从设备MISO对应引脚为PA6引脚,将主设备PA7引脚与从设备PA6引脚连接起来,此外还需将两个开发板SPI1对应的SCK引脚PA5连接起来。

    3、软件设计

    本章教程主要实现单工双向通信模式下从机从主机接收数据并将数据返回发送给主机,基本上是相当于将主机发送从机接收和从机发送主机接收两个程序结合起来。具体程序如下:
    spi.h文件
    1. #ifndef __SPI_H
    2. #define __SPI_H
    3. #include "ch32v10x.h"

    4. /* SPI Mode Definition */

    5. #define HOST_MODE    0
    6. #define SLAVE_MODE   1

    7. /* SPI Communication Mode Selection */
    8. //#define SPI_MODE   HOST_MODE

    9. #define SPI_MODE   SLAVE_MODE

    10. #define Size 6

    11. extern u8 TxData[Size];
    12. extern u8 RxData[Size];

    13. void SPI_1Lines_HalfDuplex_Init(void);

    14. #endif
    复制代码

    spi.h文件主要进行相关宏定义和函数声明;
    spi.c文件
    1. #include "spi.h"

    2. /*******************************************************************************

    3. * Function Name  : SPI_1Lines_HalfDuplex_Init

    4. * Description    : Configuring the SPI for half-duplex communication.

    5. * Input          : None

    6. * Return         : None

    7. *******************************************************************************/

    8. void SPI_1Lines_HalfDuplex_Init(void)

    9. {
    10.     GPIO_InitTypeDef GPIO_InitStructure;
    11.     SPI_InitTypeDef  SPI_InitStructure;

    12.     RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE );

    13. #if (SPI_MODE == HOST_MODE)

    14.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    15.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    16.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    17.     GPIO_Init( GPIOA, &GPIO_InitStructure );
    18.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    19.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    20.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    21.     GPIO_Init( GPIOA, &GPIO_InitStructure );

    22. #elif (SPI_MODE == SLAVE_MODE)

    23.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    24.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    25.     GPIO_Init( GPIOA, &GPIO_InitStructure );

    26.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    27.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    28.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    29.     GPIO_Init( GPIOA, &GPIO_InitStructure );

    30. #endif

    31. #if (SPI_MODE == HOST_MODE)

    32.     SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
    33.     SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

    34. #elif (SPI_MODE == SLAVE_MODE)

    35.     SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Rx;
    36.     SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;

    37. #endif

    38.     SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    39.     SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
    40.     SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    41.     SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    42.     SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;
    43.     SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    44.     SPI_InitStructure.SPI_CRCPolynomial = 7;
    45.     SPI_Init( SPI1, &SPI_InitStructure );

    复制代码

    spi.c文件主要进行主机和从机的初始化配置。
    main.c文件
    1. /********************************** (C) COPYRIGHT *******************************

    2. * File Name          : main.c

    3. * Author             : WCH

    4. * Version            : V1.0.0

    5. * Date               : 2020/04/30

    6. * Description        : Main program body.

    7. *******************************************************************************/



    8. /*

    9. *@Note

    10. 单线半双工模式,Master/Slave 模式数据收发:

    11. Master:SPI1_SCK(PA5)、SPI1_MOSI(PA7)。

    12. Slave:SPI1_SCK(PA5)、SPI1_MISO(PA6)。



    13. 本例程演示 Master 发,Slave 收。

    14. 注:两块板子分别下载 Master 和 Slave 程序,同时上电。

    15.     硬件连线:PA5 —— PA5

    16.            PA7 —— PA6

    17. */

    18. #include "debug.h"
    19. #include "spi.h"

    20. /* Global typedef */
    21. /* Global define */
    22. /* Global Variable */

    23. volatile u8 Txval=0, Rxval=0;

    24. u8 TxData[Size] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06};

    25. u8 RxData[Size];

    26. /*******************************************************************************

    27. * Function Name  : main
    28. * Description    : Main program.
    29. * Input          : None
    30. * Return         : None

    31. *******************************************************************************/

    32. int main(void)

    33. {

    34.     u8 i=0,j=0;

    35.     Delay_Init();

    36.     USART_Printf_Init(115200);

    37.     printf("SystemClk:%d\r\n",SystemCoreClock);

    38. #if (SPI_MODE == SLAVE_MODE)

    39.     printf("SLAVE Mode\r\n");

    40.     Delay_Ms(1000);

    41. #endif

    42.     SPI_1Lines_HalfDuplex_Init();

    43. #if (SPI_MODE == HOST_MODE)

    44.     printf("HOST Mode\r\n");

    45.     Delay_Ms(2000);

    46. #endif

    47.     while(1)

    48.     {

    49. #if (SPI_MODE == HOST_MODE)

    50. ////////////////////////////////发送///////////////////////////////

    51.         while( i<Size )

    52.         {

    53.             /* 发送数据 */

    54.             if( i<Size )

    55.             {

    56.                 if( SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_TXE ) != RESET )      //发送缓冲区为空

    57.                 {

    58.                     SPI_I2S_SendData( SPI1, TxData[i] );    //发送数据

    59.                     i++;

    60.                 }

    61.             }

    62.         }

    63.         /* 单向只发模式,BIDMODE=0,RXONLY=0,关闭SPI */

    64.         while( SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_TXE ) != RESET )

    65.         {

    66.             if(SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_BSY ) == RESET)

    67.             {

    68.                 SPI_Cmd( SPI1, DISABLE );

    69.             }

    70.         }



    71. //////////////////////////////////接收///////////////////////////////

    72.         Delay_Ms(5000);

    73.         SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Rx);

    74.         SPI_Cmd( SPI1, ENABLE );

    75.         while(( j<Size ))

    76.         {

    77.             /* 接收数据 */

    78.             if( j<Size )

    79.             {

    80.                 if( SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_RXNE ) != RESET )     //接收缓冲区非空

    81.                 {

    82.                     RxData[j] = SPI_I2S_ReceiveData( SPI1 );    //接收数据

    83.                     j++;

    84.                 }

    85.                 /* 单向只接收模式,关闭SPI的软件流程,P470 */

    86.                 if( j == 4 )

    87.                 {

    88.                     Delay_Us(2);

    89.                 }

    90.                 if( j == 5 )

    91.                 {

    92.                     SPI_Cmd( SPI1, DISABLE );       //关闭SPI1

    93.                 }

    94.             }

    95.         }

    96.         for( j=0; j<Size; j++ )

    97.         {

    98.             printf( "Rxdata:%02x\r\n", RxData[j] );

    99.         }

    100. #elif (SPI_MODE == SLAVE_MODE)

    101. ////////////////////////////////接收///////////////////////////////

    102.         while(( i<Size ))

    103.         {

    104.             /* 接收数据 */

    105.             if( i<Size )

    106.             {

    107.                 if( SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_RXNE ) != RESET )     //接收缓冲区非空

    108.                 {

    109.                     RxData[i] = SPI_I2S_ReceiveData( SPI1 );    //接收数据

    110.                     i++;

    111.                 }

    112.             }

    113.         }



    114.         for( i=0; i<Size; i++ )

    115.         {

    116.             printf( "Rxdata:%02x\r\n", RxData[i] );

    117.         }

    118.         SPI_Cmd( SPI1, DISABLE );       //关闭SPI1

    119. //////////////////////////////////发送///////////////////////////////

    120.         printf( "切换传输方向\r\n" );

    121.         SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Tx);

    122.         SPI_Cmd( SPI1, ENABLE );

    123.         printf( "切换传输方向完成\r\n" );

    124.         while( j<Size )

    125.         {

    126.             /* 发送数据 */

    127.             if( j<Size )

    128.             {

    129.                 if( SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_TXE ) != RESET )      //发送缓冲区为空

    130.                 {

    131.                     SPI_I2S_SendData( SPI1, RxData[j] );    //发送数据

    132.                     j++;

    133.                 }

    134.             }

    135.         }

    136.         /* 等待TXE=1,BSY=0,等待发送完成,关闭SPI1 */

    137.         while( SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_TXE ) != RESET )

    138.         {

    139.             if( SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_BSY ) == RESET )

    140.             {

    141.                 SPI_Cmd( SPI1, DISABLE );       //关闭SPI1

    142.             }

    143.         }



    144.         for( j=0; j<Size; j++ )

    145.         {

    146.             printf( "TxData:%02x\r\n", RxData[j] );

    147.         }

    148.         while(1);



    149. #endif

    150.     }

    151. }
    复制代码

    main.c文件主要进行函数初始化以及数据收发。关于主机接收,此处需要注意的是,如果在最后一个数据传输后关闭SPI模块,需进行以下操作进行处理,保证SPI不会开始一次新的传输:
    1、等待倒数第二个RXNE=1;
    2、在关闭SPI之前等待一个SPI时钟周期,此处可以利用延时函数进行等待;
    3、在进入停机模式或关闭该模块时钟之前等待最后一个RXNE=1。
    此外,当主机和从机进行接收方向的切换时,需要调用SPI_BiDirectionalLineConfig函数。

    4、下载验证

    将编译好的程序分别在主机模式和从机模式下下载到两个开发版,并将主机的PA7、PA5引脚与从机PA6、PA5引脚进行连接,开发板上电后,串口打印如下:
    CH32V CH573单片机芯片-第一百零九章:CH32V103应用教程——SPI-单线双工互发互收risc-v单片机中文社区(1)
    CH32V CH573单片机芯片-第一百零九章:CH32V103应用教程——SPI-单线双工互发互收risc-v单片机中文社区(2) SPI-单线双工互发互收.rar (472.02 KB, 下载次数: 14)






    上一篇:恭喜CH32V307-第十七届全国大学智能汽车竞赛竞速比赛规则2022
    下一篇:CH32V103 FreeRTOS相关例程
    RISCV作者优文
    全球首家只专注于RISC-V单片机行业应用的中文网站
    回复

    使用道具 举报

    高级模式
    B Color Image Link Quote Code Smilies

    本版积分规则

    关闭

    RISC-V单片机中文网上一条 /2 下一条



    版权及免责声明|RISC-V单片机中文网 |网站地图

    GMT+8, 2024-4-20 14:42 , Processed in 0.778711 second(s), 49 queries .

    快速回复 返回顶部 返回列表