当在单片机编程中多处使用同一种资源时,用C++编程会方便的多。
例如:在STM32中使用了软件I2C程序,C++头文件代码如下:
#ifndef __I2C_SOFTWARE_H__
#define __I2C_SOFTWARE_H__
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
// I2C函数使用的宏定义
#define PORT_SDA_I2C_SET_OUT sdaSetOut()
#define PORT_SDA_I2C_SET_IN sdaSetInput()
#define PORT_SDA_I2C_SET_1 HAL_GPIO_WritePin(pGpioPortSDA,gpioPinSDA,GPIO_PIN_SET)
#define PORT_SDA_I2C_SET_0 HAL_GPIO_WritePin(pGpioPortSDA,gpioPinSDA,GPIO_PIN_RESET)
#define PORT_SDA_I2C_READ HAL_GPIO_ReadPin(pGpioPortSDA,gpioPinSDA)
#define PORT_SCL_I2C_SET_OUT sclSetOut()
#define PORT_SCL_I2C_SET_1 HAL_GPIO_WritePin(pGpioPortSCL,gpioPinSCL,GPIO_PIN_SET)
#define PORT_SCL_I2C_SET_0 HAL_GPIO_WritePin(pGpioPortSCL,gpioPinSCL,GPIO_PIN_RESET)
#define PORT_SCL_I2C_READ HAL_GPIO_ReadPin(pGpioPortSCL,gpioPinSCL)
#define TimeDelay 10
class CI2C_Software
{
public:
/// @brief SCL端口定义,PORT + PIN
GPIO_TypeDef *pGpioPortSCL;
uint16_t gpioPinSCL;
/// @brief SDA端口定义,PORT + PIN
GPIO_TypeDef *pGpioPortSDA;
uint16_t gpioPinSDA;
protected:
// 修改方向用的结构
GPIO_InitTypeDef* mpSclStruct;
GPIO_InitTypeDef* mpSdaStruct;
protected:
void sdaSetInput() {
mpSdaStruct->Mode = GPIO_MODE_INPUT;
mpSdaStruct->Pull = GPIO_PULLUP;/*上拉*/
HAL_GPIO_Init(pGpioPortSDA, mpSdaStruct);
}
void sdaSetOut() {
mpSdaStruct->Mode = GPIO_MODE_OUTPUT_OD;
HAL_GPIO_Init(pGpioPortSDA, mpSdaStruct);
}
void sdaSetValue(GPIO_PinState value){
HAL_GPIO_WritePin(pGpioPortSDA,gpioPinSDA,value);
}
// void sdaSetOutput() {
// mpSclStruct->Mode = GPIO_MODE_AF_OD;
// mpSclStruct->Pull = GPIO_PULLUP;/*上拉*/
// mpSclStruct->
// HAL_GPIO_Init(pGpioPortSCL, mpSclStruct);
// }
void sclSetValue(GPIO_PinState value) {
HAL_GPIO_WritePin(pGpioPortSCL, gpioPinSCL, value);
}
public:
CI2C_Software(); // 无参数构造函数
CI2C_Software(GPIO_TypeDef *pPortScl,uint16_t pinScl,GPIO_TypeDef *pPortSda,uint16_t pinSda, GPIO_InitTypeDef* pSclInitDef,GPIO_InitTypeDef* pSdaInitDef);// 给定SCL和SDA两个信号线的“端口”和“线序”的构造函数,一般使用这个构造函数构造本类的对象
void I2C_Start(); // 生成主机I2C起始时序函数
void I2C_SendAddress(uint8_t address,uint8_t isRead); // 主机I2C发送地址和读/写方向函数
void I2C_SendData(uint8_t data);// 主机I2C发送数据函数
void I2C_Stop(); // 主机生成I2C停止时序函数
void I2C_Ack(); // 生成主机I2C应答时序函数
void I2C_NAck(); // 生成主机I2C不应答时序函数
uint8_t I2C_WaitAck(); // 主机等待
uint8_t I2C_ReadDate(uint8_t ack);
};
// 公开程序体中定义的2个软件 I2C 对象,供其它模块调用
extern CI2C_Software I2CSoftware1;
extern CI2C_Software I2CSoftware2;
#ifdef __cplusplus
}
#endif
#endif
软件I2C程序体代码如下:
#include "main.h"
#include "I2CSoftware.hpp"
#include "tim.h"
// 初始化I2C1修改方向的数据结构
GPIO_InitTypeDef gScl1InitTypeDef = { 0,GPIO_MODE_OUTPUT_OD,GPIO_PULLUP,GPIO_SPEED_FREQ_HIGH };
GPIO_InitTypeDef gSda1InitTypeDef = { 0,GPIO_MODE_INPUT,GPIO_PULLUP,GPIO_SPEED_FREQ_HIGH } ;
// 初始化修改I2C2方向的数据结构
GPIO_InitTypeDef gScl2InitTypeDef = { 0,GPIO_MODE_OUTPUT_OD,GPIO_PULLUP,GPIO_SPEED_FREQ_HIGH };
GPIO_InitTypeDef gSda2InitTypeDef = { 0,GPIO_MODE_INPUT,GPIO_PULLUP,GPIO_SPEED_FREQ_HIGH } ;
// 建立两个I2C对象,用于建立和I2C器件的连接
CI2C_Software I2CSoftware1(MCU_SCL1_GPIO_Port, MCU_SCL1_Pin,MCU_SDA1_GPIO_Port,MCU_SDA1_Pin,&gScl1InitTypeDef,&gSda1InitTypeDef);
CI2C_Software I2CSoftware1(MCU_SCL2_GPIO_Port, MCU_SCL2_Pin,MCU_SDA2_GPIO_Port,MCU_SDA2_Pin,&gScl2InitTypeDef,&gSda2InitTypeDef);
/// @brief 无参构造函数
CI2C_Software::CI2C_Software()
{
mpSclStruct = NULL;
mpSdaStruct = NULL;
}
/// @brief 给定I2C 2个信号线参数和方向数据结构体的构造函数
/// @param pPortScl :SCL端口
/// @param pinScl :SCL引脚号
/// @param pPortSda :SDA端口
/// @param pinSda :SDA引脚号
/// @param pSclInitDef :SCL修改端口属性的“数据结构”
/// @param pSdaInitDef :SDA修改端口属性的“数据结构”
CI2C_Software::CI2C_Software(GPIO_TypeDef *pPortScl,uint16_t pinScl,GPIO_TypeDef *pPortSda,uint16_t pinSda,
GPIO_InitTypeDef* pSclInitDef,GPIO_InitTypeDef* pSdaInitDef)
{
// 赋值引脚端口和引脚号
pGpioPortSCL = pPortScl;
gpioPinSCL = pinScl;
pGpioPortSDA = pPortSda;
gpioPinSDA = pinSda;
// 赋值寄存器结构体
mpSclStruct = pSclInitDef;
mpSdaStruct = pSdaInitDef;
}
/// @brief I2C Start 函数
void CI2C_Software::I2C_Start(){
PORT_SDA_I2C_SET_OUT;
PORT_SCL_I2C_SET_1;
delay_us(TimeDelay);
PORT_SDA_I2C_SET_1;
delay_us(TimeDelay);
PORT_SDA_I2C_SET_0;
delay_us(TimeDelay);
PORT_SCL_I2C_SET_0;
delay_us(TimeDelay);
}
/// @brief I2C发送一个数据,高字节在前
/// @param data :给定的数据字节
void CI2C_Software::I2C_SendData(uint8_t data){
uint8_t mask = 0x80;
PORT_SDA_I2C_SET_OUT;
for (uint8_t t = 0; t < 8; t++) {
if (data & mask)
PORT_SDA_I2C_SET_1;
else
PORT_SDA_I2C_SET_0;
mask >>= 1;
delay_us(TimeDelay);
PORT_SCL_I2C_SET_1;
delay_us(TimeDelay);
PORT_SCL_I2C_SET_0;
delay_us(TimeDelay);
}
delay_us(TimeDelay);
}
/// @brief I2C发送地址
/// @param address : 7Bit地址
/// @param isRead : 0=Write;1=Read
void CI2C_Software::I2C_SendAddress(uint8_t address,uint8_t isRead){
I2C_SendData((address << 1) | isRead);
}
/// @brief 从I2C读取一个字节
/// @param ack : 0=不应答,1=应答
uint8_t CI2C_Software::I2C_ReadDate(uint8_t ack) {
uint8_t receive = 0;
uint8_t mask = 0x80;
PORT_SDA_I2C_SET_IN;
for (uint8_t i = 0; i < 8; i++) {
PORT_SCL_I2C_SET_0;
delay_us(TimeDelay);
PORT_SCL_I2C_SET_1;
delay_us(TimeDelay);
receive <<= 1;
if (PORT_SDA_I2C_READ) receive |= mask;
mask >>= 1;
delay_us(TimeDelay);
}
if (ack==0)
I2C_NAck();
else
I2C_Ack();
delay_us(TimeDelay);
return receive;
}
void CI2C_Software::I2C_Stop(){
PORT_SDA_I2C_SET_OUT;
PORT_SCL_I2C_SET_0;
PORT_SDA_I2C_SET_0;
delay_us(TimeDelay);
PORT_SCL_I2C_SET_1;
delay_us(TimeDelay);
PORT_SDA_I2C_SET_1;
delay_us(TimeDelay);
}
void CI2C_Software::I2C_Ack() {
PORT_SCL_I2C_SET_0; //SCL拉低
PORT_SDA_I2C_SET_OUT;
PORT_SDA_I2C_SET_0;
delay_us(TimeDelay);
PORT_SCL_I2C_SET_1;
delay_us(TimeDelay);
PORT_SCL_I2C_SET_0;
delay_us(TimeDelay);
}
void CI2C_Software::I2C_NAck() {
PORT_SCL_I2C_SET_0;
PORT_SDA_I2C_SET_OUT;
PORT_SDA_I2C_SET_1;
delay_us(TimeDelay);
PORT_SCL_I2C_SET_1;
delay_us(TimeDelay);
PORT_SCL_I2C_SET_0;
}
/// @brief 等待数控电位器应答
/// @return 0=应答正常,0xFF=无应答
uint8_t CI2C_Software::I2C_WaitAck() {
uint8_t ucErrtime = 0;
PORT_SDA_I2C_SET_IN;
delay_us(TimeDelay);
PORT_SCL_I2C_SET_1;
delay_us(TimeDelay);
while (PORT_SDA_I2C_READ)
{
ucErrtime++;
if (ucErrtime > 250) {
I2C_Stop();
return 0xFF;
}
}
PORT_SCL_I2C_SET_0;
return 0;
}
函数delay_us(TimeDelay);自己另行定义