本帖最后由 RISCVLAR 于 2021-1-6 14:44 编辑
CH32V103应用教程——I2C-PEC,主机接收从机发送
本章教程主要在前面第44章的基础上进行PEC模式下的主机接收从机发送实验。 注意,本章例程使用CH32V103硬件IIC。
1、I2C简介及相关函数介绍 关于I2C包错误校验(PEC),在前面第44章已经介绍,在此不再赘述。 关于CH32V103各模式介绍以及具体信息,可参考CH32V103应用手册。I2C标准库函数在第十四章节已介绍,在此不再赘述。
2、硬件设计 本章教程使用开发板硬件I2C进行主机接收从机发送实验,需用到两个开发板,将两个开发板对应IIC引脚连接起来即可。 注意:此处需要将开发板I2C引脚外接上拉电阻。
3、软件设计 本章教程在前面第44章基础上使用硬件I2C在PEC模式下进行主机接收从机发送实验,具体程序如下: 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 );
void I2C1_ER_IRQHandler(void );
#endif
iic.h文件主要进行相关宏定义和函数声明; iic.c文件 #include "iic.h"
void I2C1_ER_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;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE );
GPIO_PinRemapConfig(GPIO_Remap_I2C1, ENABLE);
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_InitTSturcture.I2C_ClockSpeed = bound;
I2C_InitTSturcture.I2C_Mode = I2C_Mode_I2C;
I2C_InitTSturcture.I2C_DutyCycle = I2C_DutyCycle_16_9;
I2C_InitTSturcture.I2C_OwnAddress1 = address;
I2C_InitTSturcture.I2C_Ack = I2C_Ack_Enable;
I2C_InitTSturcture.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_Init( I2C1, &I2C_InitTSturcture );
#if (I2C_MODE == SLAVE_MODE)
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init( &NVIC_InitStructure );
I2C_ITConfig( I2C1, I2C_IT_ERR, ENABLE );
#endif
I2C_Cmd( I2C1, ENABLE );
I2C_CalculatePEC( I2C1, ENABLE );
#if (I2C_MODE == HOST_MODE)
I2C_AcknowledgeConfig( I2C1, ENABLE );
#endif
}
/*******************************************************************************
* Function Name : I2C1_ER_IRQHandler
* Description : This function IIC PEC error exception.
* Input : None
* Output : None
* Return : None
********************************************************************************/
void I2C1_ER_IRQHandler(void )
{
if( I2C_GetITStatus( I2C1, I2C_IT_PECERR ) != RESET )
{
printf( "PECEER\r\n" );
I2C_ClearITPendingBit( I2C1, I2C_IT_PECERR );
}
}
iic.c文件主要包括两个函数,一个是IIC初始化配置函数,包括硬件IIC对应GPIO引脚的初始化配置、IIC的初始化配置以及从机模式下的NVIC配置;一个是中断服务函数,由CH32V103应用手册可知,每个I2C模块都有两种中断向量,分别是事件中断和错误中断,此处为错误中断,中断源为PECERR,当PEC发生错误异常时进入中断服务函数。 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];
/*******************************************************************************
* Function Name : main
* Description : Main program.
* Input : None
* Return : None
*******************************************************************************/
int main(void)
{
u8 i=0;
u8 pecValue;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
Delay_Init();
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n",SystemCoreClock);
#if (I2C_MODE == HOST_MODE)
printf("IIC Host mode\r\n");
//IIC进行主机接收初始化配置
IIC_Init( 80000, RxAdderss);
//当I2C1处于空闲状态时,跳过此while循环,开启起始信号
while( I2C_GetFlagStatus( I2C1, I2C_FLAG_BUSY ) != RESET );
//开启I2C1通信起始信号
I2C_GenerateSTART( I2C1, ENABLE );
//当最后一个事件为I2C_EVENT_MASTER_MODE_SELECT事件时,说明选择I2C作为主机模式,跳过此while循环,进行下一步
while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_MODE_SELECT ) );
//发送地址来选择从机设备
I2C_Send7bitAddress( I2C1, 0x02, I2C_Direction_Receiver );
//当最后一个事件为I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED事件时,说明选择I2C进行主机接收,跳过此while循环,进行下一步数据接收
while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED ) );
while( i<7 )
{
//此循环是对发送数据前五个值的接收
if( i<5 )
{
if( I2C_GetFlagStatus( I2C1, I2C_FLAG_RXNE ) != RESET )
{
RxData[i] = I2C_ReceiveData( I2C1 );
i++;
}
}
//此段是对发送数据最后一个数据的接收以及获取当前IIC的PEC值
if( i==5 )
{
//得到当前IIC的PEC值(内部计算结果)
pecValue = I2C_GetPEC( I2C1 );
if( I2C_GetFlagStatus( I2C1, I2C_FLAG_RXNE ) != RESET )
{
RxData[i] = I2C_ReceiveData( I2C1 );
i++;
}
}
//此段是对发送数据后接一个字节的CRC8计算结果的接收
if( i==6 )
{
//在接收时:在最后一个RXNE事件之后设置I2C1_CTLR1寄存器的PEC位
I2C_TransmitPEC( I2C1, ENABLE ); //设置I2C_CR1寄存器的PEC位
if( I2C_GetFlagStatus( I2C1, I2C_FLAG_RXNE ) != RESET )
{
RxData[i] = I2C_ReceiveData( I2C1 ); //此处接收到的值为CRC8计算结果的值
i++;
}
}
}
printf( "pecValue:%02x\r\n", pecValue);
printf( "RxData:\r\n" );
for( i=0; i<7; i++ )
{
printf( "%02x\r\n", RxData[i] );
}
//当最后一个事件为I2C_EVENT_MASTER_BYTE_RECEIVED事件时,说明接收结束,跳过此while循环,进行下一步
while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED ) );
//开启I2C通信停止信号
I2C_GenerateSTOP( I2C1, ENABLE );
#elif (I2C_MODE == SLAVE_MODE)
printf("IIC Slave mode\r\n");
//IIC从机模式初始化
IIC_Init( 80000, TxAdderss);
//当最后一个事件为I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED事件时,说明选择I2C作为从机进行数据发送,跳过此while循环,进行数据发送
while( !I2C_CheckEvent( I2C1, I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED ) );
while( i<7 )
{
//此段为发送数据
if( i<6)
{
if( I2C_GetFlagStatus( I2C1, I2C_FLAG_TXE ) != RESET )
{
I2C_SendData( I2C1, TxData[i] );
i++;
}
}
//此段为发送PEC值。在发送时:在最后一个TXE事件时设置I2C1_CTLR1寄存器的PEC传输位, PEC将在最后一个 字节后被发送。
if( i==6)
{
if( I2C_GetFlagStatus( I2C1, I2C_FLAG_TXE ) != RESET )
{
//开启当前I2C1的PEC传输
I2C_TransmitPEC( I2C1, ENABLE );
i++;
}
}
}
#endif
while(1);
}
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通信,主机接收开发板串口打印情况具体如下:
|