智能家居之主机--驱动层搭建

智能家居之主机--驱动层搭建

bsp-底层驱动

bsp_gpio

 利用一个config.h的配置文件,把所有要使用的gpio的属性配置好,这样有一个好处,比较集中,也比较好查,不需要一个文件一个文件去找,在config文件里面就把pin或者属性改掉了。首先定义一个结构体

typedef struct 
{
	uint32_t RCC_GPIOx	:32;
	uint16_t gpio_pin	:16;
	uint16_t gpio_mode  :8;
	uint16_t gpio_speed	:8;
}gpio_config_t;

(讲解:后面的:<num>是定义了一个num位的变量,但是只占用其中num位个内存,这样的好处是更加节省内存,名词叫位域)
一般刚入门的不太会去考虑在写代码的时候内存不够用的情况,要么就是全局变量满天飞,要么就是可能只需要存一个布尔型的参数的时候定义了一个占用32位内存的一个参数,如果是103c8t6这种内部flash并不大的芯片来说,如果代码量比较大的话肯定是不行的;可以来做个实验

#include<stdio.h>
#include<string.h>

typedef struct{
	unsigned int a : 4;
	unsigned int b : 1;
 } test_t;
 
typedef struct{
	unsigned int a;
	unsigned int b;
 } test1_t;

test_t test;
test1_t test1;
int main()
{
	test.a = 10;
	test.b = 1;
	test1.a = 10;
	test1.b = 1;
	printf("test_size:%d\r\n",sizeof(test));
	printf("test1_size:%d\r\n",sizeof(test1));
	return 0;
 } 

两个一样的结构体,一个限制位域一个不限制,运行一下看看这两个结构体占用内存的大小情况。

test_size:4
test1_size:8

画个图就好理解了
在这里插入图片描述
这个是test结构体的存放
在这里插入图片描述
这是test1的
所以回看上面gpio的结构体,如果不定义位域的话应该是占用10个字节,但是现在只占用了8个字节,可以通过keil的map来看一下内存使用情况。
在这里插入图片描述
然后就可以通过config的形式进行配置了

/******橘色LED的GPIO配置*******/
#define ORANGE_LED_GPIO_CONFIG		\
{									\
	RCC_APB2Periph_GPIOB,			\
	GPIO_Pin_9,						\
	GPIO_Mode_Out_OD,				\
	GPIO_Speed_50MHz, 		\
}

bsp_adc

 和bsp_gpio的定义方法相似,不过结构体更加复杂了一些,直接上代码;

typedef struct 
{
	uint32_t RCC_ADCx	:32;
	uint32_t adc_mode	:20;
	FunctionalState adc_scanconvmode  :1;
	FunctionalState adc_continuousconvmode	:1;
	uint32_t adc_externaltrigconv	:20;
	uint32_t adc_dataalign	:12;
	uint8_t  adc_nbrofchannel	:4;
	uint8_t Rank	:4; 
	uint8_t ADC_SampleTime	:4;
}adc_config_t;

那么相应的配置就是

/******MQ_2ADC*******/
#define MQ_2_ADC_CONFIG			\
{								\
	RCC_APB2Periph_ADC1,		\
	ADC_Mode_Independent,		\
	DISABLE,					\
	DISABLE,					\
	ADC_ExternalTrigConv_None,	\
	ADC_DataAlign_Right,		\
	1,							\
	1,							\
	ADC_SampleTime_239Cycles5,	\
}

再来看看map文件adc占用的内存字节数
在这里插入图片描述
那么bsp的ADC初始化函数就可以这样写

/**
 * 名称: BSP_ADC_Init
 * 功能:ADC初始化函数
 * 输入: 
 * @ADC_TypeDef: ADC序号
 * @adc_config_t: ADC相关配置
**/
void BSP_ADC_Init(ADC_TypeDef* ADCx,adc_config_t adc_config)
{
	ADC_InitTypeDef ADC_InitStructure;
	RCC_APB2PeriphClockCmd(adc_config.RCC_ADCx,ENABLE);
	
	ADC_DeInit(ADCx);
	
	ADC_InitStructure.ADC_Mode = adc_config.adc_mode;	
	ADC_InitStructure.ADC_ScanConvMode = adc_config.adc_scanconvmode;	
	ADC_InitStructure.ADC_ContinuousConvMode = adc_config.adc_continuousconvmode;	
	ADC_InitStructure.ADC_ExternalTrigConv = adc_config.adc_externaltrigconv;	
	ADC_InitStructure.ADC_DataAlign = adc_config.adc_dataalign;	
	ADC_InitStructure.ADC_NbrOfChannel = adc_config.adc_nbrofchannel;	
	ADC_Init(ADCx, &ADC_InitStructure);	 
  
	ADC_Cmd(ADCx, ENABLE);	
	
	ADC_ResetCalibration(ADCx);	
	 
	while(ADC_GetResetCalibrationStatus(ADCx));	
	
	ADC_StartCalibration(ADCx);	
 
	while(ADC_GetCalibrationStatus(ADCx));	 
}

后面定义初始化ADC的时候就不需要一大堆了,只需要调用这个函数把参数传进去即可。

bsp_uart

  串口结构体定义:

typedef struct
{
	uint32_t RCC_USARTx			:32;
	uint32_t bound 				:32;
	uint16_t wordlength 		:16;
	uint16_t stopbit			:16;
	uint16_t parity				:12;
	uint16_t hwflowcontrol		:12;
	uint16_t mode				:4; 
	uint8_t  irqchannel			:8; 
	uint8_t  irqpreemption		:8; 
	uint8_t	 irqSub				:8; 
}uart_config_t;
/**
 * 名称: BSP_UART_Init
 * 功能:串口初始化函数
 * 输入: 
 * @USART_TypeDef: 串口序号
 * @uart_config_t: 串口相关配置
**/
void BSP_UART_Init(USART_TypeDef* USARTx,uart_config_t uart_config)
{
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	if(USARTx == USART1)
		RCC_APB2PeriphClockCmd(uart_config.RCC_USARTx,ENABLE);
	else
		RCC_APB1PeriphClockCmd(uart_config.RCC_USARTx,ENABLE);
	
	USART_InitStructure.USART_BaudRate = uart_config.bound;
	USART_InitStructure.USART_WordLength = uart_config.wordlength;
	USART_InitStructure.USART_StopBits = uart_config.stopbit;
	USART_InitStructure.USART_Parity = uart_config.parity;
	USART_InitStructure.USART_HardwareFlowControl = uart_config.hwflowcontrol;
	USART_InitStructure.USART_Mode = uart_config.mode;	
	USART_Init(USARTx, &USART_InitStructure); 
	 
	USART_ITConfig(USARTx, USART_IT_RXNE, ENABLE);
	
	USART_Cmd(USARTx, ENABLE); 

	NVIC_InitStructure.NVIC_IRQChannel = uart_config.irqchannel;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=uart_config.irqpreemption;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =uart_config.irqSub;		
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			
	NVIC_Init(&NVIC_InitStructure);	
}

注意由于串口1和串口2需要开的RCC时钟不同,底层直接区分开感觉会方便点;

bsp_timer

 同理直接上代码吧

typedef struct
{
	uint32_t period 		:16; //自动重装载值
	uint16_t Prescaler		:16; //定时器分频
	uint16_t countermode	:8;  //计数方式
	uint16_t clockdivision	:12; //时钟分割
	
	uint8_t irq_channel		:8;  //中断类型
	uint8_t irq_preemption	:8;  //抢占优先级
	uint8_t irq_subpriority :8;  //子优先级
	bool    enble_sta		:1;	 //是否使能定时器
}timer_config_t;
/**
 * 名称: BSP_TIME_Init
 * 功能:定时器初始化函数
 * 输入: 
 * @RCC_TIMx: 定时器时钟
 * @TIM_TypeDef: 定时器序号
 * @timer_config_t: 定时器相关配置
**/
void BSP_TIME_Init(uint32_t RCC_TIMx,TIM_TypeDef* TIMx,timer_config_t timer_config)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_TIMx,ENABLE);  
	
	TIM_TimeBaseInitStructure.TIM_Period = timer_config.period; 	
	TIM_TimeBaseInitStructure.TIM_Prescaler=timer_config.Prescaler;  
	TIM_TimeBaseInitStructure.TIM_CounterMode=timer_config.countermode; 
	TIM_TimeBaseInitStructure.TIM_ClockDivision=timer_config.clockdivision; 
	
	TIM_TimeBaseInit(TIMx,&TIM_TimeBaseInitStructure);
	
	TIM_ITConfig(TIMx,TIM_IT_Update,ENABLE); 
	 
	
	NVIC_InitStructure.NVIC_IRQChannel=timer_config.irq_channel; 
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=timer_config.irq_preemption; 
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=timer_config.irq_subpriority; 
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStructure);
	
	TIM_Cmd(TIMx,timer_config.enble_sta);
}

伪调度

 定时器初始化后就可以开始搭建伪调度了,首先定义一个伪调度的结构体;

typedef struct
{
	void(*task_func)(void);
	uint16_t rate_hz;
	uint16_t interval_ticks;
	uint32_t last_run;
}sched_task_t;

将每个任务和轮询时间定义好后放入放入结构体中;

static sched_task_t sched_task[]=
{
	{loop_1ms,1000,0,0},
	{loop_10ms,100,0,0},
	{loop_100ms,10,0,0},
	{loop_1s,1,0,0},
};

我定义了这些时间段的任务,然后进行轮询任务的轮询结束时间进行计算;

void loop_check(void)
{
	u8 index = 0;
	for(index = 0;index <task_num;index++ )
	{
		sched_task[index].interval_ticks = 1000/sched_task[index].rate_hz;
		if(sched_task[index].interval_ticks < 1)
			sched_task[index].interval_ticks = 1;
	}
}

然后就可以开始轮询任务了,通过duty_loop函输,通过当前的时间和结束时间还有轮询时间进行对比,如果大于就执行对应函数;

void duty_loop(void)
{
	u8 index = 0;
	for(index = 0;index < task_num;index++)
	{
		uint32_t tnow = get_sys_time_ms();
		if(tnow - sched_task[index].last_run >= sched_task[index].interval_ticks)
		{
			sched_task[index].last_run = tnow;
			sched_task[index].task_func();
		}	
	}
}

然后我们就可以在loop_1s函数里面加入一个LED亮灭的函数,不需要使用delay函数看看是否能够1s切换。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值