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文件 - #ifndef __IIC_H
- #define __IIC_H
- #include "ch32v10x_conf.h"
- /* I2C Mode Definition */
- #define HOST_MODE 0
- #define SLAVE_MODE 1
- /* I2C Communication Mode Selection */
- //#define I2C_MODE HOST_MODE
- #define I2C_MODE SLAVE_MODE
- /* Global define */
- #define Size 7
- #define RXAdderss 0x02
- #define TxAdderss 0x02
- void IIC_Init( u32 bound, u16 address );
- #endif
iic.h文件主要进行相关定义和函数声明; iic.c文件 - #include "iic.h"
- extern u8 RxData[Size]; //接收缓存
- extern u8 TxData[Size]; //发送缓存
- u8 RxCount = 0x00; //接收计数
- u8 TxCount = 0x00; //发送计数
- u8 Rec_Finish_Flag = 0x00; //接收完成标志
- void I2C1_EV_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
- /*******************************************************************************
- * Function Name : IIC_Init
- * Description : Initializes the IIC peripheral.
- * Input : None
- * Return : None
- *******************************************************************************/
- void IIC_Init( u32 bound, u16 address )
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- I2C_InitTypeDef I2C_InitTSturcture;
- NVIC_InitTypeDef NVIC_InitStructure;
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE );
- GPIO_PinRemapConfig(GPIO_Remap_I2C1, ENABLE); //重映射功能,PB8和PB9重映射为I2C1
- RCC_APB1PeriphClockCmd( RCC_APB1Periph_I2C1, ENABLE );
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init( GPIOB, &GPIO_InitStructure );
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init( GPIOB, &GPIO_InitStructure );
- I2C_DeInit(I2C1);
- I2C_InitTSturcture.I2C_ClockSpeed = bound; //设置I2C传输速率
- I2C_InitTSturcture.I2C_Mode = I2C_Mode_I2C; //指定I2C工作模式
- I2C_InitTSturcture.I2C_DutyCycle = I2C_DutyCycle_16_9; //指定时钟占空比
- I2C_InitTSturcture.I2C_OwnAddress1 = address; //指定I2C自身设备地址
- I2C_InitTSturcture.I2C_Ack = I2C_Ack_Enable; //使能或者关闭响应 (一般都是使能)
- I2C_InitTSturcture.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //指定地址的长度,可以是7位或10位
- I2C_Init( I2C1, &I2C_InitTSturcture );
- #if (I2C_MODE == SLAVE_MODE)
- /* 中断分组初始化 */
- NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn; //事件中断
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init( &NVIC_InitStructure );
- NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn; //错误中断
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init( &NVIC_InitStructure );
- I2C_ITConfig( I2C1, I2C_IT_EVT|I2C_IT_ERR, ENABLE ); //使能事件中断
- #endif
- I2C_Cmd( I2C1, ENABLE );
- #if (I2C_MODE == HOST_MODE)
- I2C_AcknowledgeConfig( I2C1, ENABLE );
- #endif
- }
- #if (I2C_MODE == SLAVE_MODE)
- //下述中断程序中,当主机读取和写入数据时,都会引起地址位被置位,即发生EV1事件(即本中断处理程序中将发送和接收的EV1合并了);
- //当主机写入数据时,中断的执行顺序是EV1—>EV2—>EV4,其中有多个数据EV2会多次执行;
- //当主机读取数据时,中断的执行顺序是EV1—>EV3—>EV3-2,本中断程序中将EV3和EV3-1合并了,若有多个数据,EV3将多次执行。
- //本程序只进行从机中断接收
- //事件中断处理函数
- void I2C1_EV_IRQHandler( void )
- {
- uint16_t STAR1Register,STAR2Register;
- //状态寄存器1和状态寄存器2对应值
- STAR1Register = I2C1->STAR1;
- STAR2Register = I2C1->STAR2;
- //I2C从机 (MSL = 0)
- //若状态寄存器2的位0不为1,即为0,则代表从模式(1代表主模式)
- if( ( STAR2Register & 0x0001 ) != 0x0001 )
- {
- //从模式下,若状态寄存器1 位1 ADDR的位为1,收到的地址匹配,即 主机已发送地址,地址为被置位·(ADDR = 1: EV1(包括发送和接收))
- if( STAR1Register & 0x0002 )
- {
- //清除相应值
- STAR1Register = 0;
- STAR2Register = 0;
- RxCount = 0x00;
- TxCount = 0x00;
- }
- //从机接收到数据 (RXNE = 1: EV2)
- //从模式下,若状态寄存器1 位6 RxNE位为1,则数据寄存器非空
- if( STAR1Register & 0x0040 )
- {
- RxData[ RxCount ] = I2C1->DATAR;
- RxCount++;
- STAR1Register = 0;
- STAR2Register = 0;
- }
- //从机接收到停止信号,检测到停止条件(STOPF =1: EV4)
- //从模式下,若状态寄存器1 位4 STOPF位为1,则检测到停止条件
- if( STAR1Register & 0x0010 )
- {
- //控制寄存器1 位0设置为1,启用IIC模块
- I2C1->CTLR1 |= 0x0001;
- STAR1Register = 0;
- STAR2Register = 0;
- Rec_Finish_Flag = 0x01;
- }
- //从机发送数据 发送数据(TxE = 1: EV3、EV3-1)
- //从模式下,若状态寄存器1 位7 TxE位为1,则数据寄存器空
- if( STAR1Register & 0x0080 )
- {
- I2C1->DATAR = TxData[ TxCount ];
- TxCount++;
- STAR1Register = 0;
- STAR2Register = 0;
- }
- //从机接收非应答信号 检测到非应答(AF =1: EV3-2)
- //从模式下,若状态寄存器1 位10 AF位为1,则应答失败(当没有返回应答时,硬件将置该位为’1’)
- if( STAR1Register & 0x0400 )
- {
- I2C1->STAR1 &= ~( 0x0400 ); //该位由软件写’0’清除,或在PE=0时由硬件清除。
- I2C1->STAR1 &= ( 0xFDFF );
- STAR1Register = 0;
- STAR2Register = 0;
- }
- }
- }
- //错误中断处理函数
- //下述程序中,发送各种错误进行错误中断不做对应的处理,最后只进行清除寄存器(STAR1和STAR2)操作。
- void I2C1_ER_IRQHandler(void)
- {
- uint16_t STAR1Register,STAR2Register;
- //状态寄存器1和状态寄存器2对应值
- STAR1Register = I2C1->STAR1;
- STAR2Register = I2C1->STAR2;
- if(I2C_GetITStatus(I2C1, I2C_IT_SMBALERT))
- {
- }
- else if(I2C_GetITStatus(I2C1, I2C_IT_TIMEOUT))
- {
- }
- else if(I2C_GetITStatus(I2C1, I2C_IT_PECERR))
- {
- }
- else if(I2C_GetITStatus(I2C1, I2C_IT_OVR))
- {
- }
- else if(I2C_GetITStatus(I2C1, I2C_IT_AF))
- {
- I2C_ClearITPendingBit(I2C1, I2C_IT_AF);
- }
- else if(I2C_GetITStatus(I2C1, I2C_IT_ARLO))
- {
- }
- else if(I2C_GetITStatus(I2C1, I2C_IT_BERR))
- {
- }
- //控制寄存器1 位0设置为1,启用IIC模块
- I2C1->CTLR1 |= 0x0001;
- STAR1Register = 0;
- STAR2Register = 0;
- }
- #endif
iic.c文件主要进行硬件IIC初始化以及进行IIC接收中断函数配置,关于程序讲解,可见程序注释部分。 main.c文件 - /********************************** (C) COPYRIGHT *******************************
- * File Name : main.c
- * Author : WCH
- * Version : V1.0.0
- * Date : 2020/04/30
- * Description : Main program body.
- *******************************************************************************/
- //轮询发送、中断接收
- #include "debug.h"
- #include "iic.h"
- /* Global Variable */
- u8 TxData[Size] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
- u8 RxData[Size];
- extern __IO u8 Rec_Finish_Flag;
- /*******************************************************************************
- * Function Name : main
- * Description : Main program.
- * Input : None
- * Return : None
- *******************************************************************************/
- int main(void)
- {
- u8 i=0;
- Delay_Init();
- USART_Printf_Init(115200);
- printf("SystemClk:%d\r\n",SystemCoreClock);
- //当I2C作为主机时
- #if (I2C_MODE == HOST_MODE)
- printf("IIC Host mode\r\n");
- //I2C进行主机初始化配置
- IIC_Init( 80000, TxAdderss);
- //while(1)
- {
- //当I2C1处于空闲状态时,跳过此while循环,开启起始信号
- while( I2C_GetFlagStatus( I2C1, I2C_FLAG_BUSY ) != RESET ); //检查是否设置了指定I2C标志
- //开启I2C1通信起始信号
- I2C_GenerateSTART( I2C1, ENABLE );
- //当最后一个事件为I2C_EVENT_MASTER_MODE_SELECT事件时,说明选择I2C作为主机模式,跳过此while循环,进行下一步
- while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_MODE_SELECT ) ); //检查最后一个I2Cx事件是否等于作为参数传递的事件
- //发送地址来选择从机设备
- I2C_Send7bitAddress( I2C1, 0x02, I2C_Direction_Transmitter );
- //当最后一个事件为I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED事件时,说明选择I2C进行主机发送,跳过此while循环,进行下一步数据发送
- while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) );
- for( i = 0; i < 6; i++ )
- {
- /* 发送寄存器地址 */
- I2C_SendData( I2C1, TxData[i] );
- while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED ) );
- }
- //开启I2C通信停止信号
- I2C_GenerateSTOP( I2C1, ENABLE );
- //Delay_Ms( 1000 );
- }
- while(1);
- //当I2C作为从机时
- #elif (I2C_MODE == SLAVE_MODE)
- printf("IIC Slave mode\r\n");
- //I2C进行从机初始化配置
- IIC_Init( 80000, RXAdderss);
- while(1)
- {
- if(Rec_Finish_Flag)
- {
- for( i=0; i<6; i++ )
- {
- printf( "%02x\r\n", RxData[i] );
- }
- Rec_Finish_Flag = 0;
- }
- }
- #endif
- }
main.c文件主要进行函数初始化以及主机发送和打印从机数据。
4、下载验证 将编译好的程序下载到开发版并复位,主从之间无需同时供电,从机复位后,主机复位一次发送一次数据,而从机无需再次复位,从机串口打印如下:
|