STM32之模拟IIC总线通信(C++)

目录

前言

主要内容

头文件

辅助函数

相关信号函数

起始信号

停止信号

接收应答信号

发送应答信号

发送非应答信号

发送一个字节数据

接收一个字节数据

应用

前言

 上一篇也讲解了STM32的模拟IIC总线通信,其所使用的语言为C语言,但也有了面向对象的思想了。于是某基于该思想认为可以用C++来实现这个通信,说不定使用起来会更加方便,所以有了这一篇。另外,鄙人的C++也是临时所学,其中多少有不足指出,希望读者指教,谢谢! 

主要内容

 上一篇对程序的解说已足够详细,故此篇我尽量使其语言简短。

头文件

#include<stm32f10x.h>
#include<delay.h>
 
/*数据传输方向*/
#define DIRECTION_OUT	GPIO_Mode_Out_PP			//输出
#define DIRECTION_IN	GPIO_Mode_IN_FLOATING		//输入
 
//上拉
#define SET_SCL		(SCL_GPIO -> BSRR = SCL_Pin)
#define SET_SDA		(SDA_GPIO -> BSRR = SDA_Pin)
//下拉
#define RESET_SCL	(SCL_GPIO -> BRR = SCL_Pin)
#define RESET_SDA 	(SDA_GPIO -> BRR = SDA_Pin)
 
#define READ_SDA	(((SDA_GPIO -> IDR & SDA_Pin) != 0x00) ? 1 : 0)
 
#define IIC_DELAY delay_us(1)	//IIC延时,可根据实际情况适当调整
 
class IIC_Soft
{
	private:
		GPIO_TypeDef *SCL_GPIO;
		GPIO_TypeDef *SDA_GPIO;
		unsigned short int SCL_Pin;
		unsigned short int SDA_Pin;
 
		void IIC_SetClock();
		void IIC_DataDir(GPIOMode_TypeDef direction);
	public:
		IIC_Soft(GPIO_TypeDef *SCL_GPIOx,unsigned short int SCL_Pinx,GPIO_TypeDef *SDA_GPIOx,unsigned short int SDA_Pinx);
		void IIC_Start();
		void IIC_Stop();
		unsigned char IIC_ReceiveACK();
		void IIC_SendACK();
		void IIC_SendNACK();
		void IIC_SendByte(unsigned char dat);
		unsigned char IIC_ReadByte();
};

想必读者已经猜到我用C++写该程序的主要用意,这里所创建的类即其核心。

辅助函数

#include<iic_soft_cpp.h>
 
/******************************************************************************
	本文件为IIC的基本信号集合(作为主机),就目前测试,适合在各个频率下工作,
	针对不同的频率,仅需适当调整IIC延时时间和调整一小部分内容即可
	每个信号中都以SCL低电平为结尾(停止信号除外),这样一来可防止信号发送
	之后在SDA上的电平变化引起误触发
*******************************************************************************/
 
static GPIO_InitTypeDef GPIO_IIC;	//引脚初始化结构体
 
/*********************使能相应的端口时钟*********************/
void IIC_Soft::IIC_SetClock()
{
	unsigned int RCC_APB2Periph;
	switch((unsigned int)SCL_GPIO)
	{
		case (unsigned int)GPIOA:
			RCC_APB2Periph = RCC_APB2Periph_GPIOA;
			break;
		case (unsigned int)GPIOB:
			RCC_APB2Periph = RCC_APB2Periph_GPIOB;
			break;
		case (unsigned int)GPIOC:
			RCC_APB2Periph = RCC_APB2Periph_GPIOC;
			break;
		case (unsigned int)GPIOD:
			RCC_APB2Periph = RCC_APB2Periph_GPIOD;
			break;
		case (unsigned int)GPIOE:
			RCC_APB2Periph = RCC_APB2Periph_GPIOE;
			break;
		case (unsigned int)GPIOF:
			RCC_APB2Periph = RCC_APB2Periph_GPIOF;
			break;
		case (unsigned int)GPIOG:
			RCC_APB2Periph = RCC_APB2Periph_GPIOG;
			break;
	}
 
	switch((unsigned int)SDA_GPIO)
	{
		case (unsigned int)GPIOA:
			RCC_APB2Periph |= RCC_APB2Periph_GPIOA;
			break;
		case (unsigned int)GPIOB:
			RCC_APB2Periph |= RCC_APB2Periph_GPIOB;
			break;
		case (unsigned int)GPIOC:
			RCC_APB2Periph |= RCC_APB2Periph_GPIOC;
			break;
		case (unsigned int)GPIOD:
			RCC_APB2Periph |= RCC_APB2Periph_GPIOD;
			break;
		case (unsigned int)GPIOE:
			RCC_APB2Periph |= RCC_APB2Periph_GPIOE;
			break;
		case (unsigned int)GPIOF:
			RCC_APB2Periph |= RCC_APB2Periph_GPIOF;
			break;
		case (unsigned int)GPIOG:
			RCC_APB2Periph |= RCC_APB2Periph_GPIOG;
			break;
	}
    RCC->APB2ENR |= RCC_APB2Periph;
}
 
/******************************设置数据线(SDA)的传输方向******************************/
void IIC_Soft::IIC_DataDir(GPIOMode_TypeDef direction)
{
	GPIO_IIC.GPIO_Pin   = SDA_Pin;
	GPIO_IIC.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_IIC.GPIO_Mode  = direction;			//数据传输方向
	GPIO_Init(SDA_GPIO,&GPIO_IIC);
}
 
/*******************IIC初始化*******************/
IIC_Soft::IIC_Soft(GPIO_TypeDef *SCL_GPIOx,unsigned short int SCL_Pinx,GPIO_TypeDef *SDA_GPIOx,unsigned short int SDA_Pinx)
{
	SCL_GPIO = SCL_GPIOx;
	SCL_Pin  = SCL_Pinx;
	SDA_GPIO = SDA_GPIOx;
	SDA_Pin  = SDA_Pinx;
 
	IIC_SetClock();
	
	GPIO_IIC.GPIO_Pin = SCL_Pin;
	GPIO_IIC.GPIO_Mode  = GPIO_Mode_Out_PP;
	GPIO_IIC.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(SCL_GPIO,&GPIO_IIC);		//初始化SCL引脚
 
	IIC_DataDir(DIRECTION_OUT);			//初始化SDA引脚(输出模式)
 
	RESET_SCL;
	SET_SDA;
	SET_SCL;
}

相关信号函数

起始信号

/****IIC起始信号(SCL高电平下SDA的一个下降沿,后以SCL变为低电平收尾)****/
/*	     				   ____
						 /     \4.7us
					___/        \___									 */
void IIC_Soft::IIC_Start()
{
	IIC_DataDir(DIRECTION_OUT);	//SDA为输出模式
 
	SET_SDA;
	SET_SCL;
	RESET_SDA;
	__nop();
	__nop();	//必须延时
	RESET_SCL;
}

停止信号

/****IIC停止信号(SCL高电平下SDA一个上升沿,后以SCL变为低电平收尾)****/
/*							  _____________
					SCL ____/					
							 4us_____
					SDA ______/	     \____						   */
void IIC_Soft::IIC_Stop()
{
	IIC_DataDir(DIRECTION_OUT);	//SDA为输出模式
 
	RESET_SDA;
	SET_SCL;
	SET_SDA;
//	RESET_SCL;
}

接收应答信号

/*接收应答信号,若接收到非应答信号,则发送停止信号(SDA低电平表示应答,高电平表示非应答)*/
unsigned char IIC_Soft::IIC_ReceiveACK()
{
	unsigned char state;
	IIC_DataDir(DIRECTION_IN);	//SDA为输入模式
	
	SET_SDA;										//释放SDA
	SET_SCL;
	state = READ_SDA;
	RESET_SCL;
	if(state)										//若接收到非应答信号,发送停止信号
	{
		IIC_Stop();
	}
	return state;
}

发送应答信号

/****应答(SDA保持为低电平)****/
/*							 	 ____
					SCL _______/	 \_____				
						_____
					SDA	   	 \____________						   */
void IIC_Soft::IIC_SendACK()
{
	IIC_DataDir(DIRECTION_OUT);	//方向为输出
 
	RESET_SDA;
	SET_SCL;
	RESET_SCL;
}

发送非应答信号

/****非应答(SDA保持为高电平)****/
/*							 	 ____
					SCL _______/	 \_____				
							  _____________
					SDA	____/		 	 							*/
void IIC_Soft::IIC_SendNACK()
{
	IIC_DataDir(DIRECTION_OUT);	//方向为输出
 
	SET_SDA;
	SET_SCL;
	RESET_SCL;
}

发送一个字节数据

/****IIC发送一个字节数据(每发送一个字节接收一次应答)****/
void IIC_Soft::IIC_SendByte(unsigned char dat)
{
	unsigned char i;
	IIC_DataDir(DIRECTION_OUT);	//方向为输出
	for(i = 0;i < 8;i++)
	{
		if((dat >> 7) & 0x01)
		{
			SET_SDA;
		}
		else
		{
			RESET_SDA;
		}
 
		__nop();
		__nop();
		__nop();
		__nop();	//此处必须延时,但用微秒级的延时显得过慢,故此处用nop延时
		SET_SCL;
		RESET_SCL;
		dat <<= 1;
	}
}

接收一个字节数据

/****IIC接收一个字节(每接收一个字节发送一次应答/非应答)****/
unsigned char IIC_Soft::IIC_ReadByte()
{
	unsigned char i,receive;
	IIC_DataDir(DIRECTION_IN);	//方向为输入
	SET_SDA;										//释放数据线
 
	for(i = 0;i < 8;i++)
	{
		SET_SCL;
		receive <<= 1;
		receive |= (READ_SDA & 0x01);
		RESET_SCL;
		IIC_DELAY;		//必须延时
	}
	return receive;
}

应用

    以上便是主要的函数,下面我同样用OLED来举例子。

IIC_Soft OLED_IIC(GPIOE,GPIO_Pin_13,GPIOE,GPIO_Pin_14);		//OLED引脚初始化结构体
 
/**********************往OLED中发送一个字节(数据/命令)**********************/
/*
	mode:字节性质
		命令(OLED_CMD)
		数据(OLED_DATA)
	data:发送内容
*/
static void OLED_IIC_SendByte(unsigned char mode,unsigned char data)
{
	OLED_IIC.IIC_Start();
	OLED_IIC.IIC_SendByte((OLED_ADDRESS << 1) & 0xfe);		//direction is write
	OLED_IIC.IIC_ReceiveACK();
	OLED_IIC.IIC_SendByte(mode);
	OLED_IIC.IIC_ReceiveACK();
	OLED_IIC.IIC_SendByte(data);
	OLED_IIC.IIC_ReceiveACK();
	OLED_IIC.IIC_Stop();
}

 此处即向OLED中发送一个字节的函数,对于其他IIC外设的使用方法相似。至于其他疑问,可参考我上一篇用C语言所写的文章,谢谢!