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

news2025/7/21 1:54:13

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

  • bsp-底层驱动
    • bsp_gpio
    • bsp_adc
    • bsp_uart
    • bsp_timer
  • 伪调度

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切换。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/368650.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

fp32/fp64精度,4/8字节16进制转float/double十进制

1、IEEE-754 32位单精度浮点数&#xff08;4字节&#xff09; 1.1 32位单精度浮点数 其中&#xff0c; 32位16进制数包括1位符号位(SIGN)&#xff0c;8位指数位(EXPONENT)和 23位尾数位(MANTISSA)。 例如&#xff1a; 25.3可以表示为41CA6666&#xff08;0x41为高字节&#xf…

算法第十六期——动态规划(DP)之线性DP

【概述】 线性动态规划&#xff0c;是较常见的一类动态规划问题&#xff0c;其是在线性结构上进行状态转移&#xff0c;这类问题不像背包问题、区间DP等有固定的模板。 线性动态规划的目标函数为特定变量的线性函数&#xff0c;约束是这些变量的线性不等式或等式&#xff0c;目…

Mysql中关于查询日志的配置详解

查询日志 MySQL中的查询日志保存在文本文件中&#xff0c;能够记录MySQL中的所有数据操作。 开启查询日志 MySQL默认情况下没有开启查询日志&#xff0c;如果需要开启查询日志&#xff0c;则需要在 my.cnf 文件或者 my.ini 文件的 [mysqld] 选项下进行配置。例如&#xff0c;…

GoFrame工程目录设计介绍

GoFrame框架针对业务项目的目录设计&#xff0c;主体的思想来源于三层架构&#xff0c;但在具体实现中&#xff0c;对其进行了一定的改进和细化使其更符合工程实践和时代进步。 一.工程目录结构 GoFrame业务项目基本目录结构如下&#xff1a; 二.目录结构解释 对外接口 对…

浅谈智能电力运维管理系统在物业小区的应用分析

安科瑞 李亚俊 0引言 由于部分住宅小区存在电力设施纸质档案破损缺失、产权分界不清、查找故障点所在箱变&#xff08;箱式变电站&#xff0c;下同&#xff09;位置困难或小区出入口路径不明等情况&#xff0c;影响了抢修效率。为此&#xff0c;国网辽宁沈阳市沈北新区供电公…

IDEA Maven install Failed to execute goal org.apache.maven.plugins异常处理

目录一、异常错误二、原因三、解决方法修改pom.xml资源配置文件一、异常错误 由于服务器编译拦截了静态资源&#xff0c;导致出现异常&#xff0c;需要重新打包编译 打开IDEA带的Maven管理&#xff0c;双击clean清除由项目编译创建的target 再双击install安装jar包到本地仓库…

朴素贝叶斯笔记

贝叶斯公式在A 条件成立下&#xff0c;B的概率等于B的概率*在B条件成立下&#xff0c;A的概率/A的概率&#xff0c;推导假设一个学校中男生占总数的60%&#xff0c;女生占总数的40%。并且男生总是穿长裤&#xff0c;女生则一半穿长裤、一半穿裙子。1.正向概率。随机选取一个学生…

白盒测试方法的简单理解(通俗易懂)

白盒测试主要使用逻辑覆盖测试方法&#xff0c;包括语句覆盖、判定覆盖、条件覆盖、判定-条件覆盖、条件组合覆盖、路径覆盖等。 假设逻辑判断流程图如下图所示&#xff0c;我们简单来说说每种白盒测试方法是如何来进行的。 一、语句覆盖 语句覆盖的定义是&#xff1a;程序中…

Openwrt中动态IPV6 防火墙的正确设置方法

环境&#xff1a;光猫桥接公网IPV6 问题&#xff1a;动态IPV6地址不知道怎么设置防火墙 解决办法&#xff1a;模糊匹配前缀&#xff0c;特定后缀 背景&#xff1a;将家中光猫桥接后&#xff0c;获得了公网的IPV6地址&#xff0c;可以从外部用IPV6访问家中的设备&#xff0c;但I…

vue-cli引入wangEditor、Element,封装可上传附件的富文本编辑器组件(附源代码直接应用,菜单可调整)

关于Element安装引入&#xff0c;请参考我的另一篇文章&#xff1a;vue-cli引入Element Plus&#xff08;element-ui&#xff09;&#xff0c;修改主题变量&#xff0c;定义全局样式_shawxlee的博客-CSDN博客_chalk variables 1、安装wangeditor npm i wangeditor --savewangE…

日常编程中和日期相关的代码和bug

本文主要是Java中和日期时间相隔的几个常用代码函数代码&#xff0c;做了总结&#xff0c;希望在日常编码中&#xff0c;可以帮到大家。 1.计算闰年 记住一个短语&#xff0c;“四年一润&#xff0c;百年不闰&#xff0c;四百再润”&#xff0c;不管换啥语言&#xff0c;相信…

HyperGBM用Adversarial Validation解决数据漂移问题

本文作者&#xff1a;杨健&#xff0c;九章云极 DataCanvas 主任架构师 数据漂移问题近年在机器学习领域来越来越得到关注&#xff0c;成为机器学习模型在实际投产中面对的一个主要挑战。当数据的分布随着时间推移逐渐发生变化&#xff0c;需要预测的数据和用于训练的数据分布…

剑指 Offer 30. 包含min函数的栈

摘要 剑指 Offer 30. 包含min函数的栈 一、栈解析 package Stock;import java.util.Stack;/*** Classname JZ30min函数栈* Description TODO* Date 2023/2/24 18:59* Created by xjl*/ public class JZ30min函数栈 {/*** description 最小栈的含义是每次从栈中获取的数据都是…

AWS攻略——使用中转网关(Transit Gateway)连接不同区域(Region)VPC

文章目录Peering方案Transit Gateway方案环境准备创建Transit Gateway Peering Connection接受邀请修改中转网关路由修改被邀请方中转网关路由修改邀请方中转网关路由测试修改Public子网路由知识点参考资料区别于 《AWS攻略——使用中转网关(Transit Gateway)连接同区域(Region…

深入理解Spring Boot的自动配置

通过spring boot可以很方便的独立运行spring应用程序&#xff0c;因为spring boot内嵌了Tomcat、Jetty等servlet容器&#xff0c;切提供自动配置功能&#xff0c;无需进行XML配置即可自动运行应用程序。那么Spring boot启动过程中具体进行了哪些操作呢&#xff1f;下面是Spring…

Julia 数学函数

Julia 提供了一套高效、可移植的标准数学函数。 数值比较 下表列出了用于数值比较的函数&#xff1a; 函数测试是否满足如下性质isequal(x, y)x 与 y 值与类型是否完全相同isfinite(x)x 是否是有限大的数字isinf(x)x 是否是&#xff08;正/负&#xff09;无穷大isnan(x)x 是否…

项目重构,从零开始搭建一套新的后台管理系统

背景 应公司发展需求&#xff0c;我决定重构公司的后台管理系统&#xff0c;从提出需求建议到现在的实施&#xff0c;期间花了将近半个月的时间&#xff0c;决定把这些都记录下来。 之前的后台管理系统实在是为了实现功能而实现的&#xff0c;没有考虑到后期的扩展性&#xf…

SpringMVC常见面试题(2023最新)

目录前言1.简单介绍下你对springMVC的理解?2.说一说SpringMVC的重要组件及其作用3.SpringMVC的工作原理或流程4.SpringMVC的优点5.SpringMVC常用注解6.SpringMVC和struts2的区别7.怎么实现SpringMVC拦截器8.SpringMvc的控制器是不是单例模式&#xff1f;如果是&#xff0c;有什…

关基系统三月重保安全监测怎么做?ScanV提供纯干货!

三月重保当前&#xff0c;以政府、大型国企央企、能源、金融等重要行业和领域为代表的关键信息基础设施运营单位都将迎来“网络安全大考”。 对重要关基系统进行安全风险监测并收敛暴露面&#xff0c;响应监管要求进行安全加固&#xff0c;重保期间实时安全监测与数据汇报等具体…

js函数声明方式*2,对象声明方式*7,不同的调用

目录 一、两种函数声明方式 二、七种对象声明方式 一、两种函数声明方式 【1】命名函数 形如&#xff1a; function fu1(){ let firstName 111111111 let lastName 2222222 console.log(firstName ,lastName ) } function fu2(firstName&#xff0c;lastName ){ …