目录
前言
上一篇也讲解了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语言所写的文章,谢谢!