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

第四十四章: CH32V103应用教程——I2C-PEC,主机发送从机接收

[复制链接]

  离线 

  • TA的每日心情
    慵懒
    2021-7-23 17:16
  • 签到天数: 17 天

    [LV.4]

    发表于 2021-4-28 16:15:22 | 显示全部楼层 |阅读模式

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

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

    x
    本帖最后由 草帽王子 于 2021-9-10 17:32 编辑

    本章教程主要在前面第38章的基础上进行PEC模式下的主机发送从机接收实验。
    注意,本章例程使用CH32V103硬件IIC。


    1、I2C简介及相关函数介绍

    IIC包错误校验(PEC)是为了提供传输的可靠性而增加一项 CRC8 校验的步骤,使用以下多项式对每一位串行数据进行计算:

    C=X8+X2+X+1

    PEC 计算是由控制寄存器的 ENPEC 位激活,对所有信息字节进行计算,包括地址和读写位在内。在发送时,启用 PEC 会在最后一字节数据之后加上一个字节的 CRC8 计算结果;而在接收模式,在最后一字节被认为是 CRC8 校验结果,如果和内部的计算结果不符合,就会回复一个 NAK,如果是主接收器,无论校验结果正确与否,都会回复一个 NAK。

    关于I2C其他相关介绍,在前面章节已经介绍,在此不再赘述。

    关于CH32V103各模式介绍以及具体信息,可参考CH32V103应用手册。I2C标准库函数在第十四章节已介绍,在此不再赘述。


    2、硬件设计

    本章教程使用开发板硬件I2C进行主机发送从机接收实验,需用到两个开发板,将两个开发板对应IIC引脚连接起来即可。
    注意:此处需要将开发板I2C引脚外接上拉电阻。


    3、软件设计

    本章教程在前面第38章基础上使用硬件I2C在PEC模式下进行主机发送从机接收实验,具体程序如下:
    iic.h文件
    1. #ifndef __IIC_H
    2. #define __IIC_H

    3. #include "ch32v10x_conf.h"

    4. /* I2C Mode Definition */
    5. #define HOST_MODE   0
    6. #define SLAVE_MODE   1

    7. /* I2C Communication Mode Selection */
    8. #define I2C_MODE   HOST_MODE
    9. //#define I2C_MODE   SLAVE_MODE

    10. /* Global define */
    11. #define Size   7
    12. #define RXAdderss   0x02
    13. #define TxAdderss   0x02

    14. void IIC_Init( u32 bound, u16 address );
    15. void I2C1_ER_IRQHandler(void );

    16. #endif
    复制代码
    iic.h文件主要进行宏定义和函数声明;
    iic.c文件
    1. #include "iic.h"

    2. void I2C1_ER_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));

    3. /*******************************************************************************
    4. * Function Name  : IIC_Init
    5. * Description    : Initializes the IIC peripheral.
    6. * Input          : None
    7. * Return         : None
    8. *******************************************************************************/
    9. void IIC_Init( u32 bound, u16 address )
    10. {
    11.     GPIO_InitTypeDef GPIO_InitStructure;
    12.     I2C_InitTypeDef I2C_InitTSturcture;
    13.     NVIC_InitTypeDef NVIC_InitStructure;

    14.     RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE );
    15.     GPIO_PinRemapConfig(GPIO_Remap_I2C1, ENABLE);
    16.     RCC_APB1PeriphClockCmd( RCC_APB1Periph_I2C1, ENABLE );

    17.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    18.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    19.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    20.     GPIO_Init( GPIOB, &GPIO_InitStructure );

    21.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    22.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    23.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    24.     GPIO_Init( GPIOB, &GPIO_InitStructure );

    25.     I2C_InitTSturcture.I2C_ClockSpeed = bound;
    26.     I2C_InitTSturcture.I2C_Mode = I2C_Mode_I2C;
    27.     I2C_InitTSturcture.I2C_DutyCycle = I2C_DutyCycle_16_9;
    28.     I2C_InitTSturcture.I2C_OwnAddress1 = address;
    29.     I2C_InitTSturcture.I2C_Ack = I2C_Ack_Enable;
    30.     I2C_InitTSturcture.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    31.   I2C_Init( I2C1, &I2C_InitTSturcture );

    32. #if (I2C_MODE == SLAVE_MODE)
    33.     NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;
    34.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    35.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    36.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    37.     NVIC_Init( &NVIC_InitStructure );

    38.     I2C_ITConfig( I2C1, I2C_IT_ERR, ENABLE );

    39. #endif

    40.     I2C_Cmd( I2C1, ENABLE );
    41.     I2C_CalculatePEC( I2C1, ENABLE );

    42. #if (I2C_MODE == HOST_MODE)
    43.     I2C_AcknowledgeConfig( I2C1, ENABLE );

    44. #endif
    45. }


    46. /*******************************************************************************
    47. * Function Name  : I2C1_ER_IRQHandler
    48. * Description    : This function IIC PEC error exception.
    49. * Input          : None
    50. * Output         : None
    51. * Return         : None
    52. ********************************************************************************/
    53. void I2C1_ER_IRQHandler(void )
    54. {
    55.     if( I2C_GetITStatus( I2C1, I2C_IT_PECERR ) != RESET )
    56.     {
    57.         printf( "PECEER\r\n" );
    58.         I2C_ClearITPendingBit( I2C1, I2C_IT_PECERR );
    59.     }
    60. }
    复制代码
    iic.c文件主要包括两个函数,一个是IIC初始化配置函数,包括硬件IIC对应GPIO引脚的初始化配置、IIC的初始化配置以及从机模式下的NVIC配置;一个是中断服务函数,由CH32V103应用手册可知,每个I2C模块都有两种中断向量,分别是事件中断和错误中断,此处为错误中断,中断源为PECERR,当PEC发生错误异常时进入中断服务函数。
    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. #include "debug.h"
    9. #include "iic.h"

    10. /* Global Variable */
    11. u8 TxData[Size] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
    12. u8 RxData[Size];

    13. /*******************************************************************************
    14. * Function Name  : main
    15. * Description    : Main program.
    16. * Input          : None
    17. * Return         : None
    18. *******************************************************************************/
    19. int main(void)
    20. {
    21.     u8 i=0;
    22.     u8 pecValue;

    23.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
    24.     Delay_Init();
    25.     USART_Printf_Init(115200);
    26.     printf("SystemClk:%d\r\n",SystemCoreClock);

    27. #if (I2C_MODE == HOST_MODE)

    28.     printf("IIC Host mode\r\n");

    29.     //IIC进行主机发送初始化配置
    30.     IIC_Init( 80000, TxAdderss);

    31.     //当I2C1处于空闲状态时,跳过此while循环,开启起始信号
    32.     while( I2C_GetFlagStatus( I2C1, I2C_FLAG_BUSY ) != RESET );

    33.     //开启I2C1通信起始信号
    34.     I2C_GenerateSTART( I2C1, ENABLE );

    35.     //当最后一个事件为I2C_EVENT_MASTER_MODE_SELECT事件时,说明选择I2C作为主机模式,跳过此while循环,进行下一步
    36.     while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_MODE_SELECT ) );

    37.     //发送地址来选择从机设备
    38.     I2C_Send7bitAddress( I2C1, 0x02, I2C_Direction_Transmitter );

    39.     //当最后一个事件为I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED事件时,说明选择I2C进行主机发送,跳过此while循环,进行下一步数据发送
    40.     while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) );

    41.     while( i<7 )
    42.     {
    43.         //此段为发送数据
    44.         if( i<6)
    45.         {
    46.             if( I2C_GetFlagStatus( I2C1, I2C_FLAG_TXE ) !=  RESET )
    47.             {
    48.                 I2C_SendData( I2C1, TxData[i] );
    49.                 i++;
    50.             }
    51.         }

    52.         //此段为发送PEC值。在发送时:在最后一个TXE事件时设置I2C1_CTLR1寄存器的PEC传输位, PEC将在最后一个 字节后被发送。
    53.         if( i==6)
    54.         {
    55.             if( I2C_GetFlagStatus( I2C1, I2C_FLAG_TXE ) !=  RESET )
    56.             {
    57.                 //开启当前I2C1的PEC传输
    58.                 I2C_TransmitPEC( I2C1, ENABLE );
    59.                 i++;
    60.             }
    61.         }
    62.     }

    63.     //当最后一个事件为I2C_EVENT_MASTER_BYTE_TRANSMITTED事件时,说明发送结束,跳过此while循环,进行下一步
    64.     while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED ) );

    65.     //开启I2C通信停止信号
    66.     I2C_GenerateSTOP( I2C1, ENABLE );

    67. #elif (I2C_MODE == SLAVE_MODE)
    68.     printf("IIC Slave mode\r\n");

    69.     //IIC从机模式初始化
    70.     IIC_Init( 80000, RXAdderss);

    71.     //当最后一个事件为I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED事件时,说明选择I2C作为从机进行数据接收,跳过此while循环,进行数据接收
    72.     while( !I2C_CheckEvent( I2C1, I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED ) );

    73.     while( i<7 )
    74.     {
    75.         //此循环是对发送数据前五个值的接收
    76.         if( i<5 )
    77.         {
    78.             if( I2C_GetFlagStatus( I2C1, I2C_FLAG_RXNE ) !=  RESET )
    79.             {
    80.                 RxData[i] = I2C_ReceiveData( I2C1 );
    81.                 i++;
    82.             }
    83.         }

    84.         //此段是对发送数据最后一个数据的接收以及获取当前IIC的PEC值
    85.         if( i==5 )
    86.         {
    87.             //得到当前IIC的PEC值(内部计算结果)
    88.             pecValue = I2C_GetPEC( I2C1 );

    89.             if( I2C_GetFlagStatus( I2C1, I2C_FLAG_RXNE ) !=  RESET )
    90.             {
    91.                 RxData[i] = I2C_ReceiveData( I2C1 );
    92.                 i++;
    93.             }
    94.         }

    95.         //此段是对发送数据后接一个字节的CRC8计算结果的接收
    96.         if( i==6 )
    97.         {
    98.             //在接收时:在最后一个RXNE事件之后设置I2C1_CTLR1寄存器的PEC位
    99.             I2C_TransmitPEC( I2C1, ENABLE ); //设置I2C_CR1寄存器的PEC位

    100.             if( I2C_GetFlagStatus( I2C1, I2C_FLAG_RXNE ) !=  RESET )
    101.             {
    102.                 RxData[i] = I2C_ReceiveData( I2C1 ); //此处接收到的值为CRC8计算结果的值
    103.                 i++;
    104.             }
    105.         }
    106.     }

    107.     printf( "pecValue:%02x\r\n", pecValue);
    108.     printf( "RxData:\r\n" );

    109.     for( i=0; i<7; i++ )
    110.     {
    111.         printf( "%02x\r\n", RxData[i] );
    112.     }

    113. #endif

    114.     while(1);
    115. }

    复制代码
    main.c文件主要进行主机模式下的数据接收配置和从机模式下的数据发送配置。其中在7位地址模式下,发送的第一个字节为地址字节,头7位代表的是目标从设备地址,第8位决定了后续报文的方向,0代表是主设备写入数据到从设备,1代表是主设备向从设备读取信息。

    其中I2C_Send7bitAddress函数会根据第三个输入进行写入和读取的判断。

    关于PEC值的发送和接收,在发送时,在最后一个TXE事件时设置I2C1控制寄存器1的PEC传输位,PEC将在最后一个字节后被发送;在接收时,在最后一个RXNE事件后设置I2C1控制寄存器1的PEC传输位,在最后一字节被认为是 CRC8 校验结果,如果和内部的计算结果不符合,就会回复一个 NAK,如果是主接收器,无论校验结果正确与否,都会回复一个 NAK。


    4、下载验证

    将编译好的程序分别在主机模式和从机模式下下载到两个开发版,并将两个开发板的PB8和PB9引脚接上拉电阻然后连接起来,将两个开发板进行复位,即可进行I2C通信,从机接收开发板串口打印情况具体如下:


    CH32V CH573单片机芯片-第四十四章: CH32V103应用教程——I2C-PEC,主机发送从机接收risc-v单片机中文社区(1)

    IIC-PEC,主机发送从机接收.rar
    CH32V CH573单片机芯片-第四十四章: CH32V103应用教程——I2C-PEC,主机发送从机接收risc-v单片机中文社区(2) 43、IIC-PEC,主机发送从机接收 (1).rar (479.71 KB, 下载次数: 10)
    链接:https://pan.baidu.com/s/10cv6uZPyTqV0vL-wBG0eKw
    提取码:m807
    复制这段内容后打开百度网盘手机App,操作更方便哦







    上一篇:第四十三章:CH32V103应用教程——I2C-DMA,主机接收从机发送
    下一篇:第四十五章:CH32V103应用教程——I2C-PEC,主机接收从机发送
    RISCV作者优文
    全球首家只专注于RISC-V单片机行业应用的中文网站
    回复

    使用道具 举报

    高级模式
    B Color Image Link Quote Code Smilies

    本版积分规则

    关闭

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



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

    GMT+8, 2024-4-27 07:38 , Processed in 0.656826 second(s), 48 queries .

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