最近也是秋招收到offer了,嵌软方向,本地企业,于是闲的没事干在学校开摆,顺便提升下代码水平。
于是首当其冲的就是指针和面向对象思维,emmmm怎么说呢,这方面蛮薄弱的,于是刚学了一点结构体指针的皮毛,结合之前比赛因为用了许多I2C设备而多次反复的初始化GPIO口的麻烦,尝试了下把I2C面向对象化,顺便复习下I2C。
面向对象核心思想和C语言的实现
众所周知,向对象编程(Object-Oriented Programming,OOP)是一种通过抽象和封装来提高代码复用性和可维护性的编程范式,但是C语言他原生是面向过程语言,并未向Python和Java那样原生支持面向对象的特性。
C语言的精髓和核心是指针,通过指针直接操作内存,可以实现很多骚操作;加上C语言的struct结构体,以及typedef自定义类型,从而实现模拟类和对象;又因结构体支持结构体的嵌套,因此可以实现基于嵌套意义的类和方法继承;进而实现面向对象的思想和全部功能。
面向对象的核心思想
面向对象编程的核心包括以下几方面:
在C语言中模拟面向对象的方法
虽然 C 语言本身不支持类和对象,但可以通过结构体和函数指针模拟这些特性:
但由于C是为面向过程设计的,本质上仍然是面向过程的语言,因此类方法必须严格按照面向过程的顺序。
结构体实现封装
结构体(struct
)是 C 语言中组织数据的核心工具。可以在结构体中包含数据和函数指针,从而将操作封装为模块化的对象。
函数指针实现多态
通过在结构体中嵌入函数指针,可以动态绑定不同对象的行为。例如,不同设备可以通过函数指针调用各自的初始化和操作方法。
嵌套结构体模拟继承
使用嵌套的方式,使一个结构体拥有另一个结构体的成员,达到继承属性和方法的效果。
I2C——Inter-Integrated Circuit集成电路总线
I2C总线特性
I2C是一种广泛应用于嵌入式系统的串行通信协议,采用主从结构,通过两线完成数据传输。
I2C通讯基本过程
- 启动条件(Start):主机拉低 SDA 并拉高 SCL。
- 地址传输:主机发送从设备地址,等待应答。
- 数据读写:按字节传输数据,每字节后需要应答信号。
- 停止条件(Stop):主机拉低 SCL 后拉高 SDA。
代码实现
#ifndef __I2C_H__ #define __I2C_H__
#include "stm32f10x.h"
typedef struct SoftI2C_t{ GPIO_TypeDef *SCL_Port; uint16_t SCL_Pin; GPIO_TypeDef *SDA_Port; uint16_t SDA_Pin; uint32_t Delay;
void(*Start)(struct SoftI2C_t *self); void(*Stop)(struct SoftI2C_t *self); uint8_t(*WriteByte)(struct SoftI2C_t *self, uint8_t byte); uint8_t(*ReadByte)(struct SoftI2C_t *self, uint8_t ack);
} SoftI2C_t;
void SoftI2C_Init(SoftI2C_t *self);
#endif
|
#include "I2C.h"
#define SCL_HIGH(self) GPIO_SetBits((self)->SCL_Port, (self)->SCL_Pin) #define SCL_LOW(self) GPIO_ResetBits((self)->SCL_Port, (self)->SCL_Pin) #define SDA_HIGH(self) GPIO_SetBits((self)->SDA_Port, (self)->SDA_Pin) #define SDA_LOW(self) GPIO_ResetBits((self)->SDA_Port, (self)->SDA_Pin) #define SDA_READ(self) GPIO_ReadInputDataBit((self)->SDA_Port, (self)->SDA_Pin)
static void _SoftI2C_Delay(SoftI2C_t *self) { if (!self->Delay){ return ; } SysTick->LOAD = 72 * (self->Delay); SysTick->VAL = 0x00; SysTick->CTRL = 0x00000005; while(!(SysTick->CTRL & 0x00010000)); SysTick->CTRL = 0x00000004; }
static void _SoftI2C_GPIO_Init(SoftI2C_t *self) {
if (self->SCL_Port == GPIOB || self->SDA_Port == GPIOB) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); } if (self->SCL_Port == GPIOA || self->SDA_Port == GPIOA) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); }
GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = self->SCL_Pin; GPIO_Init(self->SCL_Port, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = self->SDA_Pin; GPIO_Init(self->SDA_Port, &GPIO_InitStructure);
SDA_HIGH(self); SCL_HIGH(self); } static void SoftI2C_Start(SoftI2C_t *self) { SDA_HIGH(self); SCL_HIGH(self); _SoftI2C_Delay(self); SDA_LOW(self); _SoftI2C_Delay(self); SCL_LOW(self); }
static void SoftI2C_Stop(SoftI2C_t *self) { SCL_LOW(self); SDA_LOW(self); _SoftI2C_Delay(self); SCL_HIGH(self); SDA_HIGH(self); _SoftI2C_Delay(self); }
static uint8_t SoftI2C_WriteByte(SoftI2C_t *self, uint8_t byte) { uint8_t ack = 0; for (uint8_t i = 0; i < 8; i++) { if (byte & 0x80) { SDA_HIGH(self); } else { SDA_LOW(self); } byte <<= 1; SCL_HIGH(self); _SoftI2C_Delay(self); SCL_LOW(self); _SoftI2C_Delay(self); }
SDA_HIGH(self); SCL_HIGH(self); _SoftI2C_Delay(self); ack = !SDA_READ(self); _SoftI2C_Delay(self); SCL_LOW(self);
return ack; }
static uint8_t SoftI2C_ReadByte(SoftI2C_t *self, uint8_t ack) { uint8_t byte = 0; SDA_HIGH(self); for (uint8_t i = 0; i < 8; i++){ byte <<= 1; SCL_HIGH(self); _SoftI2C_Delay(self); if (SDA_READ(self)){ byte |= 0x01; } SCL_LOW(self); _SoftI2C_Delay(self); } if (ack){ SDA_LOW(self); }else{ SDA_HIGH(self); } SCL_HIGH(self); _SoftI2C_Delay(self); SCL_LOW(self); SDA_HIGH(self); return byte; }
void SoftI2C_Init(SoftI2C_t *self) { _SoftI2C_GPIO_Init(self);
self->Start = SoftI2C_Start; self->Stop = SoftI2C_Stop; self->WriteByte = SoftI2C_WriteByte; self->ReadByte = SoftI2C_ReadByte; }
|