[RISC-V MCU 应用开发] 第九十九章、CH32V103应用教程——IIC-主发从收持续收发

[复制链接]
 楼主| RISCVLAR 发表于 2021-6-17 19:27 | 显示全部楼层 |阅读模式
ar, iic, I2c, ST, ni, TE
CH32V103应用教程——IIC-主发从收持续收发

前面章节介绍过硬件IIC主从之间的通信,但其需要复位一次进行一次通信。本章教程主要前面章节的基础上,进行硬件IIC之间的持续通信,无需进行复位。

1、IIC简介
关于IIC,在前面章节已经进行过介绍,在此不再赘述。
本章教程主要在第三十七章主机发送从机接收的基础上进行改进,主机发送程序与前面类似,本章主要对从机接收方法进行改进,主要使用中断接收方式。从机中断函数中具体接收流程如下:EV1—>EV2—>EV4,具体介绍如下:
EV1:ADDR=1,读STAR1然后读STAR2后将清除该事件;
EV2:RxNE=1,读DATAR将清除该事件;
EV4:STOPF=1,读STAR1然后写CTLR1寄存器将清除该事件。
具体执行方法和步骤可见程序具体内容。
关于TIM具体介绍,可参考前面章节及CH32V103应用手册。

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

3软件设计
本章教程主要进行硬件IIC持续收发实验,具体程序如下:
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. #endif
iic.h文件主要进行相关定义和函数声明;
iic.c文件
  1. #include "iic.h"

  2. extern u8 RxData[Size];               //接收缓存
  3. extern u8 TxData[Size];               //发送缓存

  4. u8 RxCount = 0x00;                    //接收计数
  5. u8 TxCount = 0x00;                    //发送计数

  6. u8 Rec_Finish_Flag = 0x00;            //接收完成标志

  7. void I2C1_EV_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));

  8. /*******************************************************************************
  9. * Function Name  : IIC_Init
  10. * Description    : Initializes the IIC peripheral.
  11. * Input          : None
  12. * Return         : None
  13. *******************************************************************************/
  14. void IIC_Init( u32 bound, u16 address )
  15. {
  16.     GPIO_InitTypeDef GPIO_InitStructure;
  17.     I2C_InitTypeDef  I2C_InitTSturcture;
  18.     NVIC_InitTypeDef NVIC_InitStructure;

  19.     RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE );
  20.     GPIO_PinRemapConfig(GPIO_Remap_I2C1, ENABLE); //重映射功能,PB8和PB9重映射为I2C1
  21.     RCC_APB1PeriphClockCmd( RCC_APB1Periph_I2C1, ENABLE );

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

  26.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  27.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
  28.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  29.     GPIO_Init( GPIOB, &GPIO_InitStructure );

  30.     I2C_DeInit(I2C1);
  31.     I2C_InitTSturcture.I2C_ClockSpeed = bound;     //设置I2C传输速率
  32.     I2C_InitTSturcture.I2C_Mode = I2C_Mode_I2C;    //指定I2C工作模式
  33.     I2C_InitTSturcture.I2C_DutyCycle = I2C_DutyCycle_16_9;  //指定时钟占空比
  34.     I2C_InitTSturcture.I2C_OwnAddress1 = address;  //指定I2C自身设备地址
  35.     I2C_InitTSturcture.I2C_Ack = I2C_Ack_Enable;   //使能或者关闭响应 (一般都是使能)
  36.     I2C_InitTSturcture.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;  //指定地址的长度,可以是7位或10位
  37.     I2C_Init( I2C1, &I2C_InitTSturcture );

  38. #if (I2C_MODE == SLAVE_MODE)
  39.     /* 中断分组初始化 */
  40.     NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;  //事件中断
  41.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;   //抢占优先级
  42.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //子优先级
  43.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  44.     NVIC_Init( &NVIC_InitStructure );

  45.     NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;  //错误中断
  46.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;   //抢占优先级
  47.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //子优先级
  48.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  49.     NVIC_Init( &NVIC_InitStructure );

  50.     I2C_ITConfig( I2C1, I2C_IT_EVT|I2C_IT_ERR, ENABLE );   //使能事件中断
  51. #endif

  52.     I2C_Cmd( I2C1, ENABLE );

  53. #if (I2C_MODE == HOST_MODE)
  54.     I2C_AcknowledgeConfig( I2C1, ENABLE );

  55. #endif
  56. }


  57. #if (I2C_MODE == SLAVE_MODE)

  58. //下述中断程序中,当主机读取和写入数据时,都会引起地址位被置位,即发生EV1事件(即本中断处理程序中将发送和接收的EV1合并了);
  59. //当主机写入数据时,中断的执行顺序是EV1—>EV2—>EV4,其中有多个数据EV2会多次执行;
  60. //当主机读取数据时,中断的执行顺序是EV1—>EV3—>EV3-2,本中断程序中将EV3和EV3-1合并了,若有多个数据,EV3将多次执行。
  61. //本程序只进行从机中断接收
  62. //事件中断处理函数
  63. void I2C1_EV_IRQHandler( void )
  64. {
  65.     uint16_t STAR1Register,STAR2Register;

  66.     //状态寄存器1和状态寄存器2对应值
  67.     STAR1Register = I2C1->STAR1;
  68.     STAR2Register = I2C1->STAR2;

  69.     //I2C从机 (MSL = 0)
  70.     //若状态寄存器2的位0不为1,即为0,则代表从模式(1代表主模式)
  71.     if( ( STAR2Register & 0x0001 ) != 0x0001 )
  72.     {
  73.         //从模式下,若状态寄存器1 位1 ADDR的位为1,收到的地址匹配,即 主机已发送地址,地址为被置位·(ADDR = 1: EV1(包括发送和接收))
  74.         if( STAR1Register & 0x0002  )
  75.         {
  76.             //清除相应值
  77.             STAR1Register = 0;
  78.             STAR2Register = 0;
  79.             RxCount = 0x00;
  80.             TxCount = 0x00;
  81.         }

  82.         //从机接收到数据 (RXNE = 1: EV2)
  83.         //从模式下,若状态寄存器1 位6 RxNE位为1,则数据寄存器非空
  84.         if( STAR1Register & 0x0040 )
  85.         {
  86.             RxData[ RxCount ] = I2C1->DATAR;
  87.             RxCount++;
  88.             STAR1Register = 0;
  89.             STAR2Register = 0;
  90.         }

  91.         //从机接收到停止信号,检测到停止条件(STOPF =1: EV4)
  92.         //从模式下,若状态寄存器1 位4 STOPF位为1,则检测到停止条件
  93.         if( STAR1Register & 0x0010 )
  94.         {
  95.             //控制寄存器1 位0设置为1,启用IIC模块
  96.             I2C1->CTLR1 |= 0x0001;
  97.             STAR1Register = 0;
  98.             STAR2Register = 0;
  99.             Rec_Finish_Flag = 0x01;
  100.         }


  101.         //从机发送数据   发送数据(TxE = 1: EV3、EV3-1)
  102.         //从模式下,若状态寄存器1 位7 TxE位为1,则数据寄存器空
  103.         if( STAR1Register & 0x0080 )
  104.         {
  105.             I2C1->DATAR = TxData[ TxCount ];
  106.             TxCount++;
  107.             STAR1Register = 0;
  108.             STAR2Register = 0;
  109.         }

  110.         //从机接收非应答信号  检测到非应答(AF =1: EV3-2)
  111.         //从模式下,若状态寄存器1 位10 AF位为1,则应答失败(当没有返回应答时,硬件将置该位为’1’)
  112.         if( STAR1Register & 0x0400 )
  113.         {

  114.             I2C1->STAR1 &= ~( 0x0400 );  //该位由软件写’0’清除,或在PE=0时由硬件清除。
  115.             I2C1->STAR1 &= ( 0xFDFF );
  116.             STAR1Register = 0;
  117.             STAR2Register = 0;
  118.         }
  119.     }
  120. }


  121. //错误中断处理函数
  122. //下述程序中,发送各种错误进行错误中断不做对应的处理,最后只进行清除寄存器(STAR1和STAR2)操作。
  123. void I2C1_ER_IRQHandler(void)
  124. {

  125.     uint16_t STAR1Register,STAR2Register;

  126.     //状态寄存器1和状态寄存器2对应值
  127.     STAR1Register = I2C1->STAR1;
  128.     STAR2Register = I2C1->STAR2;

  129.     if(I2C_GetITStatus(I2C1, I2C_IT_SMBALERT))
  130.     {
  131.     }
  132.     else if(I2C_GetITStatus(I2C1, I2C_IT_TIMEOUT))
  133.     {
  134.     }
  135.     else if(I2C_GetITStatus(I2C1, I2C_IT_PECERR))
  136.     {
  137.     }
  138.     else if(I2C_GetITStatus(I2C1, I2C_IT_OVR))
  139.     {
  140.     }
  141.     else if(I2C_GetITStatus(I2C1, I2C_IT_AF))
  142.     {
  143.         I2C_ClearITPendingBit(I2C1, I2C_IT_AF);
  144.     }
  145.     else if(I2C_GetITStatus(I2C1, I2C_IT_ARLO))
  146.     {
  147.     }
  148.     else if(I2C_GetITStatus(I2C1, I2C_IT_BERR))
  149.     {

  150.     }
  151.     //控制寄存器1 位0设置为1,启用IIC模块
  152.     I2C1->CTLR1 |= 0x0001;
  153.     STAR1Register = 0;
  154.     STAR2Register = 0;
  155. }
  156. #endif
iic.c文件主要进行硬件IIC初始化以及进行IIC接收中断函数配置,关于程序讲解,可见程序注释部分。
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. #include "debug.h"
  10. #include "iic.h"

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

  14. extern __IO u8 Rec_Finish_Flag;

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

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

  27. //当I2C作为主机时
  28. #if (I2C_MODE == HOST_MODE)
  29.     printf("IIC Host mode\r\n");

  30.     //I2C进行主机初始化配置
  31.     IIC_Init( 80000, TxAdderss);

  32.     //while(1)
  33.     {
  34.     //当I2C1处于空闲状态时,跳过此while循环,开启起始信号
  35.     while( I2C_GetFlagStatus( I2C1, I2C_FLAG_BUSY ) != RESET ); //检查是否设置了指定I2C标志

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

  38.     //当最后一个事件为I2C_EVENT_MASTER_MODE_SELECT事件时,说明选择I2C作为主机模式,跳过此while循环,进行下一步
  39.     while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_MODE_SELECT ) );  //检查最后一个I2Cx事件是否等于作为参数传递的事件

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

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

  44.     for( i = 0; i < 6; i++ )
  45.     {
  46.         /* 发送寄存器地址 */
  47.         I2C_SendData( I2C1, TxData[i]  );
  48.         while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED ) );
  49.     }

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

  52.     //Delay_Ms( 1000 );
  53.     }

  54.     while(1);

  55. //当I2C作为从机时
  56. #elif (I2C_MODE == SLAVE_MODE)
  57.     printf("IIC Slave mode\r\n");

  58.     //I2C进行从机初始化配置
  59.     IIC_Init( 80000, RXAdderss);

  60.     while(1)
  61.     {
  62.         if(Rec_Finish_Flag)
  63.         {
  64.             for( i=0; i<6; i++ )
  65.             {
  66.                printf( "%02x\r\n", RxData[i] );
  67.             }
  68.             Rec_Finish_Flag = 0;
  69.         }
  70.     }

  71. #endif

  72. }
main.c文件主要进行函数初始化以及主机发送和打印从机数据。

4下载验证
将编译好的程序下载到开发版并复位,主从之间无需同时供电,从机复位后,主机复位一次发送一次数据,而从机无需再次复位,从机串口打印如下:
图片1.png

98、IIC-主发从收持续收发.rar

483.32 KB, 下载次数: 59

luck刘备 发表于 2021-12-2 19:05 | 显示全部楼层
有没有中断发送,中断接收的呢?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

133

主题

296

帖子

44

粉丝
快速回复 在线客服 返回列表 返回顶部

133

主题

296

帖子

44

粉丝
快速回复 在线客服 返回列表 返回顶部