单片机面向对象编程思路和方法--框架01

一、单片机程序框架

1、轮流执行

int main (void)

{

 while(1)

 {

  sing();

  dance();

  play();

 }

}

  1. 函数sing执行的时间比较长的话,函数dance就不能很快的被执行。任何一个函数死掉的话就会影响整个系统。

2、前后台

在使用 51、AVR、STM32 单片机裸机的时候一般都是在main函数里面用while(1)做一个大循环来完成所有的处理,即应用程序是一个无限的循环,循环中调用相应的函数完成所需的处理。有时候我们也需要中断中完成一些处理。相对于多任务系统而言,这个就是单任务系统,也称作前后台系统,中断服务函数作为前台程序,大循环while(1)作为后台程序。

单片机面向对象编程思路和方法--框架01_第1张图片

对应的编程代码大概是这样的:

void EXTI_IRQHandler()
{
    flag = 1;
}

int main (void)
{
    while(1)
    {
        if (flag = 1)
        {
            do_something();
            flag = 0;
        }
    }
}

有什么问题?

前后台系统的实时性差,前后台系统各个任务(应用程序)都是排队等着轮流执行,不管你这个程序现在有多紧急,没轮到你就只能等着!相当于所有任务(应用程序)的优先级都是一样的。但是前后台系统简单啊,资源消耗也少啊!在稍微大一点的嵌入式应用中前后台系统就明显力不从心了。

二、单片机是如何通过软件代码一层一层直到控制硬件底层的

1、寄存器与驱动层

        我们有过51 32等的单片机芯片的学习经历,很自然的直到硬件的底层控制来自于程序对寄存器的操作,也就意味着寄存器是软件和硬件之间交流的桥梁。

        通过学习了像stm32,gd32等芯片,我们知道了稍微高级一点的芯片包含的寄存器实在是太多太复杂了,原因是包含的片下外设太多太复杂了,所以官方为了简化我们开发使用的难度,于是就有了标准库HAL库,LL库等库函数,这些库函数也叫做驱动函数,是直接驱动底层寄存器的操作的。

        所以我们可以把这一层叫做驱动层

2、应用层与逻辑代码

        应用层就是基于驱动层的函数接口,根据我们使用者的需求从而开发的。这一层需要开发者自己开发。比如实现I2C_SOFT时序的实现,我们可以调用DRIVER库中的GPIO驱动接口。

//置位与清零SCL管脚
#define	SET_SCL	HAL_GPIO_WritePin(SHT30_SCL_GPIO_Port,SHT30_SCL_Pin,GPIO_PIN_SET) 
#define	CLR_SCL	HAL_GPIO_WritePin(SHT30_SCL_GPIO_Port,SHT30_SCL_Pin,GPIO_PIN_RESET)
//置位与清零SDA管脚
#define	SET_SDA	HAL_GPIO_WritePin(SHT30_SDA_GPIO_Port,SHT30_SDA_Pin,GPIO_PIN_SET)
#define	CLR_SDA	HAL_GPIO_WritePin(SHT30_SDA_GPIO_Port,SHT30_SDA_Pin,GPIO_PIN_RESET)
//读SDA管脚状态
#define READ_SDA	HAL_GPIO_ReadPin(SHT30_SDA_GPIO_Port,SHT30_SDA_Pin)
static void Start(void)
{
	//SCL为高电平,SDA的下降沿为I2C起始信号
	SET_SDA;
	SET_SCL;
	I2C_Delay_us(1);
	
	CLR_SDA;
	I2C_Delay_us(10);
	
	CLR_SCL;
	I2C_Delay_us(1);
}
static ACK_Value_t Write_Byte(uint8_t WR_Byte)
{
	uint8_t i;
	ACK_Value_t  ACK_Rspond;
	
	//SCL为低电平时,SDA准备数据,接着SCL为高电平,读取SDA数据
	//数据按8位传输,高位在前,利用for循环逐个接收
	for(i=0;i<8;i++)
	{
		//SCL清零,主机SDA准备数据
		CLR_SCL;
		I2C_Delay_us(1);
		if((WR_Byte&BIT7) == BIT7)
		{
			SET_SDA;
		}
		else
		{
			CLR_SDA;
		}
		I2C_Delay_us(1);
		//SCL置高,传输数据
		SET_SCL;
		I2C_Delay_us(10);
		
		//准备发送下一比特位
		WR_Byte <<= 1;
	}
	
	CLR_SCL;	
	//释放SDA,等待从机应答
	SET_SDA;
	I2C_Delay_us(1);
	
	SET_SCL;
	I2C_Delay_us(10);
	
	ACK_Rspond = (ACK_Value_t)READ_SDA;
	
	CLR_SCL;
	I2C_Delay_us(1);
	
	//返回从机的应答信号
	return ACK_Rspond;
}

/*
	* @name   Write_Byte
	* @brief  I2C写字节
	* @param  ACK_Value -> 主机回应值
	* @retval 从机返回值      
*/
static uint8_t Read_Byte(ACK_Value_t ACK_Value)
{
	uint8_t RD_Byte = 0,i;
		
	接收数据
	//SCL为低电平时,SDA准备数据,接着SCL为高电平,读取SDA数据
	//数据按8位传输,高位在前,利用for循环逐个接收
	for(i=0;i<8;i++)
	{
		//准备接收下一比特位
		RD_Byte <<= 1;
		
		//SCL清零,从机SDA准备数据
		CLR_SCL;
		I2C_Delay_us(10);
		
		//SCL置高,获取数据
		SET_SCL;
		I2C_Delay_us(10);	

		RD_Byte |= READ_SDA;		
	}
	
	
	//SCL清零,主机准备应答信号
	CLR_SCL;
	I2C_Delay_us(1);
	
	//主机发送应答信号	
	if(ACK_Value == ACK)
	{
		CLR_SDA;
	}
	else
	{
		SET_SDA;	
  }	
	I2C_Delay_us(1);
	
	
	SET_SCL; 	
	I2C_Delay_us(10);
	
	//Note:
  //释放SDA数据线
	//SCL先清零,再释放SDA,防止连续传输数据时,从机错将SDA释放信号当成NACk信号
	CLR_SCL;
  SET_SDA; 	
	I2C_Delay_us(1);

	//返回数据
	return RD_Byte;
}

         如上的两段代码,我们可以看到用宏定义的形式重命名了底层GPIO驱动函数, 然后在我们的逻辑代码中添加。从而实现时序层接口的完成 。

3、应用层

        我们想的是通过在主函数调用应用层的函数接口,从而直接获取温度。所以我们只需要在SHT30的读取温度函数里面直接调用I2C数据读取

static ACK_Value_t Write_Byte(uint8_t WR_Byte) 

从而获取数据,并根据手册的数据处理要求,对这个数据进行处理得出最终温度值

单片机面向对象编程思路和方法--框架01_第2张图片

你可能感兴趣的:(单片机,嵌入式硬件)