STM32(DMA、DHT11)

1、DMA(数据的搬运工)

DMA,全称为:Direct Memory Access,即直接存储器访问。DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路,能使 CPU 的效率大为提高。

1.1 DMA作用

DMA的传输方式不需要CPU参与,可以直接控制传输

DMA给外部设备和内存开辟一条直接传输数据的通道

目的:给CPU节省资源,使CPU的工作效率提高。

1.2 DMA主要特性

中文手册(140页)、对此页进行讲解

    1)同一个DMA模块可以有多个优先级请求:很高、高、中等、低

    2)每个通道有3个事件标志: DMA半传输、DMA传输完成、 DMA传输出错

    3)数据源 目标源 数据传输宽度对齐

    4)传输数据 字节(8位) 半字(16位) 全字(32位 )

    5)存储器<->存储器、外设<->存储器、外设<->外设

    6)闪存(flash) 、SRAM、 APB 、AHB 、外设均可以作为源或者目标

    7)搬移数据的最大长度为65535字节

1.3 DMA寄存器

    DMA_CPARx :设置外设地址的寄存器

    DMA_CMARx :设置存储器地址的寄存器

    DMA_CCRx :设置数据传输方向

    DMA_CNDTRx:设置传输的数据量

1.4 DMA的增量或者循环模式

   1)增量:外设搬移到存储器的时候 ,不希望覆盖上一个数据,会将内存设置为增量模式

    2)循环:DMA不停循环的搬移数据,一组的数据传输完成时,计数寄存器将会自动地被恢复成配置该通道时设置的初值。

1.5 DMA中断

每个 DMA 通道都可以在 DMA 传输过半、传输完成和传输错误时产生中断。为应用的灵活性考虑,通过设置寄存器的不同位来打开这些中断

实验1:DMA-ADC串口发送

实验要求:使用DMA通过串口打印ADC采集的光照值

从外设-----》内存

半字(16位)也就是每次搬16位数据,而我们的ADC每次刚好是将数据存在16位的寄存器里面,因此不需要更改。

什么时候搬移?

再ADC转化完成之后再搬移(用DMA搬移)

怎么用?

HAL_StatusTypeDef HAL_ADC_Start_DMA  (ADC_HandleTypeDef * hadc, uint32_t * pData, uint32_t  Length)

功能:启动ADC开始转换,并通过DMA搬移转换结果。

参数:ADC_HandleTypeDef * hadc 句柄

           uint32_t * pData 数据存放地址

           uint32_t  Length 数据长度

此时CPU并未参与ADC转换完成的数据读取工作,节省了CPU的资源。

实验2:DMA-ADC串口发送--按键中断

2.1 实验要求

在按键按下之后,使用DMA通过串口发送ADC采集的光照值(时钟设置成64MHz)

2.2 Cube MX 环境配置

在上一个项目基础上配置

2.3 代码编写

void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == GPIO_PIN_8)
	{
		HAL_ADC_Start_DMA(&hadc1,(uint32_t *)&buf,1);
	}
}
int fputc(int ch ,FILE* p)
{
	while(!(USART1->ISR&(1<<7)));
	USART1->TDR=ch;
	return ch;
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
	HAL_ADC_Stop_DMA(&hadc1);
	printf("%d\n",buf);
}

2.4 再加上按键的ADC值

void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == GPIO_PIN_8)
	{
		HAL_ADC_Start_DMA(&hadc1,(uint32_t *)buf,2);
	}
}
int fputc(int ch ,FILE* p)
{
	while(!(USART1->ISR&(1<<7)));
	USART1->TDR=ch;
	return ch;
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
	HAL_ADC_Stop_DMA(&hadc1);
	printf("key=%d light=%d\n",buf[0],buf[1]);
}

3、DMA不定长接收

3.1 Cube MX 环境配置 

3.2 可能使用到的函数

找函数去HAL英文手册 HAL_UART_Generic Driver 587
1HAL_UART_Receive_DMA(UART_HandleTypeDef * huart, 
uint8_t * pData, uint16_t Size) //开启DMA通道并设定通道长度
2__HAL_UART_ENABLE_IT(__HANDLE__,__INTERRUPT__)//开启串口空闲中断
3__HAL_UART_GET_FLAG(__HANDLE__,__FLAG__)//获得串口空闲中断标志
4__HAL_UART_CLEAR_FLAG(__HANDLE__,__FLAG__) //清除串口空闲中断
5HAL_UART_DMAStop(UART_HandleTypeDef * huart) //关闭串口DMA通道
6)设定的传输长度-剩余传输数量(DMA_CNDTRx)=实际长度
7HAL_UART_Transmit_DMA(UART_HandleTypeDef * huart, uint8_t * pData, uint16_t Size)  //使用DMA通道发送指定长度的字符到串口中

3.3 代码编写

main.c

uint8_t buf[128];															//接收缓冲区
uint8_t trans_buf[]="家玉是帅哥,猛哥是王子";	//发送内容

  /* USER CODE BEGIN WHILE */
  while (1)
  {
		HAL_UART_Transmit_DMA(&huart1,trans_buf,sizeof(trans_buf));		//DMA发送
		HAL_Delay(200);
		HAL_UART_Receive_DMA(&huart1,buf,128);						//接收DMA使能
		__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);				//开启串口空闲中断
    /* USER CODE END WHILE */

串口中断函数(中断向量表跳转一次)

extern uint8_t buf[128];
uint8_t len = 0;
// 直接在串口中断函数编写代码,不采用回调函数
// 因为DMA自动搬数据,有中断后进入该函数,直接写即可
void USART1_IRQHandler(void)	
{
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
	// 在中断线中判断IDLE状态
    // 1->空闲
    // param1->串口;
    // param2->Idle line detection interrupt->空闲线路检测中断
	if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE))	
	{                               // 1. 获得串口空闲中断标志	
		__HAL_UART_CLEAR_FLAG(&huart1,UART_CLEAR_IDLEF);													 // 2. 清除空闲中断标志
		HAL_UART_DMAStop(&huart1);	 // 3. 清除->空闲->停止DMA		
		len = 128 - hdma_usart1_rx.Instance->CNDTR;
            // 4. 设定的传输长度-剩余传输数量(DMA_CNDTRx)=实际长度
		HAL_UART_Transmit(&huart1, buf, len, 100);
                                    // 5. 数据处理——发送
		HAL_UART_Receive_DMA(&huart1, buf, 128);
								     // 6. 重新开启DMA
	
	}
  /* USER CODE END USART1_IRQn 1 */
}

4、DHT11

查看说明书

DHT11说明书081206.pdf

DHT11是一款有已校准数字信号输出的温湿度传感器。 其精度湿度±5%RH, 温度±2℃,量程湿度20-90%RH, 温度0~50℃。

DHT11温湿度传感器类似于DS18B20采用一线制通信协议(单总线),所谓“一线制”顾名思义,设备与上位控制器通信使用1根线,这根线同时承担了时钟和数据线的角色。    

硬件上的简单势必会带来软件上的复杂,像一线制通信协议,一般都是上位CPU先发开始、复位等电平信号,然后DHT11发送回应信号,然后再发送对应数据,上位CPU接收电平脉冲信号,连续接收固定的字节,然后再进行解析数据。 

协议分析

       主机先要发送一个至少18ms的低电平,在这个过程中,DHT11内部完成AD转换等操作,当主机拉高后,有20-40us时间,这个时间用于主机做输入输出切换,当主机释放总线控制权(此时主机为输入状态,总线被上拉电阻拉高),DHT11尝试将总线拉低,成功拉低后就开始准备发送数据了,再拉高一次就开始传输数据了。

40位:8湿度整  8湿度小  8温度整  8温度小  8校验和

  1. 读取一位  (函数)
  2. 循环8次(读取一位)
  3. 循环5次第二步的函数
  4. 检验和 = 8湿度整+8湿度小+8温度整+8温度小

查看原理图

代码分析

//设置IO为输入模式
static void DHT11_IO_IN(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    GPIO_InitStruct.Pin = DHT11_GPIO_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStruct);
}
//设置IO为输出模式
static void DHT11_IO_OUT(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    GPIO_InitStruct.Pin = DHT11_GPIO_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStruct);
}

//复位DHT11      \\起始信号
void DHT11_Rst(void)
{
    DHT11_IO_OUT(); 	//SET OUTPUT  转换成输出模式
    HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, GPIO_PIN_RESET); 	
     //拉低DQ
    HAL_Delay(20);    	//拉低至少18ms
    HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, GPIO_PIN_SET);		      
     //拉高DQ
    delay_us(30);     	//主机拉高20~40us
}

//等待DHT11的回应
//返回1:未检测到DHT11的存在
//返回0:存在
uint8_t DHT11_Check(void)
{
    uint8_t retry=0;
    DHT11_IO_IN();//SET INPUT
    while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~80us
    {
        retry++;
        delay_us(1);
    };
    if(retry>=100)return 1;
    else retry=0;
    while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~80us
    {
        retry++;
        delay_us(1);
    };
    if(retry>=100)return 1;
    return 0;
}

//从DHT11读取一个位
//返回值:1/0
uint8_t DHT11_Read_Bit(void)
{
    uint8_t retry=0;
    while(DHT11_DQ_IN&&retry<100)//等待变为低电平
    {
        retry++;
        delay_us(1);
    }//延时100
    retry=0;
    while(!DHT11_DQ_IN&&retry<100)//等待变高电平
    {
        retry++;
        delay_us(1);
    }
    delay_us(40);//等待40us
    if(DHT11_DQ_IN)return 1;
    else return 0;
}

//从DHT11读取一个字节
//返回值:读到的数据
uint8_t DHT11_Read_Byte(void)
{
    uint8_t i,dat;
    dat=0;
    for (i=0; i<8; i++)
    {
        dat<<=1;
        dat|=DHT11_Read_Bit();
    }
    return dat;
}

//从DHT11读取一次数据
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:HAL_OK,正常;1,读取失败
uint8_t DHT11_Read_Data(uint8_t *humiH,uint8_t *humiL,uint8_t *tempH,uint8_t *tempL)
{
    uint8_t buf[5];
    uint8_t i;
    DHT11_Rst();
    if(DHT11_Check()==0)
    {
        for(i=0; i<5; i++) //读取40位数据
        {
            buf[i]=DHT11_Read_Byte();
        }
        if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
        {
            *humiH=buf[0];     //湿度高八位,整数位
            *humiL=buf[1];      //湿度低八位,小数位
            *tempH=buf[2];     //温度高八位,整数位
            *tempL=buf[3];      //温度低八位,小数位
        }
    } else
        return HAL_ERROR;
    return HAL_OK;
}

//初始化DHT11的IO口 DQ 同时检测DHT11的存在
//返回1:不存在
//返回0:存在
uint8_t FS_DHT11_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    GPIO_InitStruct.Pin = DHT11_GPIO_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStruct);
    HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, GPIO_PIN_SET);	
    // 输出高电平
    DHT11_Rst();  //复位DHT11
    return DHT11_Check();//等待DHT11的回应
}


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32开发基础知识之位操作、宏定义、ifdef条件编译、extern变量申明、typedef类型别名、结构体

一、引言 本文将对STM32入门开发的基本C语言基础知识进行回顾和总结&#xff0c;一边学者在开发过程中能较顺利地进行。主要包括位操作、define宏定义、ifdef条件编译、extern变量申明、typedef类型别名、结构体等基本知识。 二、基础C语言开发知识总结 &#xff08;一&…

25、pytest的测试报告插件allure

allure简介 在这里&#xff0c;你将找到使用allure创建、定制和理解测试报告所需的一切。开始让你的测试沟通更清晰&#xff0c;更有影响力。 Allure Report是一个实用程序&#xff0c;它处理由兼容的测试框架收集的测试结果并生成HTML报告。 安装allure 1、确保安装了Java…

java实验:数据库应用(idea+mysql+php)设计用户注册和登录

设计用户注册和登录界面&#xff0c;实现用户注册和登录操作。 设计用户注册/登录界面;使用工具在MySQL中创建user表&#xff0c;包括学号、姓名、密码、专业、班级&#xff1b;实现注册操作&#xff1a;在user表中插入一条新纪录&#xff0c;但学号不能重复&#xff1b;实现登…

Python爬虫技术:如何利用ip地址爬取动态网页

目录 一、引言 二、Python爬虫基础 三、动态网页结构分析 四、利用ip地址爬取动态网页 1、找到需要爬取的动态网页的URL结构 2、构造请求参数 3、发送请求并获取响应 4、解析响应内容 五、实例代码 六、注意事项 七、总结 一、引言 随着互联网的快速发展&#xff0…

python爬虫混肴DES案例:某影视大数据平台

声明&#xff1a; 该文章为学习使用&#xff0c;严禁用于商业用途和非法用途&#xff0c;违者后果自负&#xff0c;由此产生的一切后果均与作者无关 一、找出需要加密的参数 js运行atob(‘aHR0cHM6Ly93d3cuZW5kYXRhLmNvbS5jbi9Cb3hPZmZpY2UvQk8vTW9udGgvb25lTW9udGguaHRtbA’…

Mysql分布式集群部署---MySQL集群Cluster将数据分成多个片段,每个片段存储在不同的服务器上

1.1 目的 部署MysqlCluster集群环境 1.2 MySQL集群Cluster原理 1 数据分片 MySQL集群Cluster将数据分成多个片段&#xff0c;每个片段存储在不同的服务器上。这样可以将数据负载分散到多个服务器上&#xff0c;提高系统的性能和可扩展性。 2. 数据同步 MySQL集群Cluster使…

Proteus的网络标号与总线

Proteus为了减少过多、复杂的连线&#xff0c;可以使用网络标号与总线配合使用。 Proteus的导线上添加了网络标号&#xff0c;意味着在Proteus上相同的网络标号是连在一起的&#xff0c;所说在图纸上看不出来。 如下图是比较好的Proteus中使用总线的绘制的图纸。可以效仿着画…

【Linux】mkdir 命令使用

mkdir命令 mkdir&#xff08;英文全拼&#xff1a;make directory&#xff09;命令用于创建目录。 著者 作者&#xff1a;David MacKenzie。 mkdir命令 -Linux手册页 语法 mkdir [参数] [文件名] 命令选项及作用 执行令 &#xff1a; mkdir --help 执行命令结果 参数 …

HttpRunner4 Python版(十二)自动化测试平台 实战开发接入案例 技术实现 功能逻辑大致梳理 实行方案初稿

前言 通过之前的文档相信你对HttpRunner 4.x Python版本以后有较为深入的理解和认识了,本文主要讲解 动化测试平台 实战开发接入案例 技术实现 功能逻辑大致梳理 实行方案初稿,后续具体案例需要根据自身项目组的功能去具体实现,并在日常维护工作中逐步完善并增加其健壮性。 …

Leetcode刷题详解——单词拆分

1. 题目链接&#xff1a;139. 单词拆分 2. 题目描述&#xff1a; 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。 **注意&#xff1a;**不要求字典中出现的单词全部都使用&#xff0c;并且字典中的单词可以重复使用。…

C#,数值计算——计算实对称矩阵所有特征值与特征向量的三角分解与QL迭代法源程序

1 文本格式 using System; namespace Legalsoft.Truffer { /// <summary> /// Computes all eigenvalues and eigenvectors of a real symmetric matrix by /// reduction to tridiagonal form followed by QL iteration. /// </summary> pu…

Qlik 成为网络犯罪的焦点

研究人员警告说&#xff0c;Cactus 勒索软件组织正在利用 Qlik Sense 数据可视化、探索和监控解决方案中的关键漏洞来获得对企业网络的初始访问权限。 今年八月下旬&#xff0c;Qlik Sense 开发人员 针对影响 Windows 版本平台的两个关键漏洞发布了补丁 。 其中一个漏洞 CVE-…

【高效开发工具系列】云服务器+Nginx自定义图床

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Oracle-数据库连接数异常上涨问题分析

问题&#xff1a; 用户的数据库在某个时间段出现连接数异常上涨问题&#xff0c;时间持续5分钟左右&#xff0c;并且问题期间应用无法正常连接请求数据库 从连接数的监控上可以看到数据库平常峰值不到100个连接&#xff0c;在问题时间段突然上涨到400以上 问题分析&#xff1a;…

6页手写笔记总结信号与系统常考知识大题知识点

题型一 判断系统特性题型二 求系统卷积题型三 求三大变换正反变换题型四 求全响应题型五 已知微分方程求系统传递函数题型六 已知系统的传递函数求微分方程题型七 画出系统的零极点图&#xff0c;并判断系统的因果性和稳定性 &#xff08;笔记适合快速复习&#xff0c;可能会有…

Spring-AOP

目录 一、引入AOP 二、核心AOP概念和术语 三、切点表达式 四、Spring实现AOP &#xff08;一&#xff09;AspectJ的支持 1 、基于注解开发 1.1 引入依赖 1.2 实现目标类 1.3 定义切面类&#xff08;日志管理&#xff09; 1.4 将目标类和切面类纳入Spring容器 1.5 开…

分布式搜索引擎(Elastic Search)+消息队列(RabbitMQ)部署

一、分布式搜索引擎&#xff1a;Elastic Search Elastic Search的目标就是实现搜索。是一款非常强大的开源搜索引擎&#xff0c;可以帮助我们从海量数据中快速找到需要的内容。在数据量少的时候&#xff0c;我们可以通过索引去搜索关系型数据库中的数据&#xff0c;但是如果数…

MySQL-含json字段表和与不含json字段表查询性能对比

含json字段表和与不含json字段表查询性能对比 说明: EP_USER_PICTURE_INFO_2:不含json字段表 20200729json_test:含有json字段表 其中20200729json_test 标准ID、MANAGER_NO、PHONE_NO 为非json字段 data为json字段 2个表中MANAGER_NO、PHONE_NO都创建了各自的索引 测试…

iphone/安卓手机如何使用burp抓包

iphone 1. 电脑 ipconfig /all 获取电脑网卡ip&#xff1a; 192.168.31.10 2. 电脑burp上面打开设置&#xff0c;proxy&#xff0c;增加一条 192.168.31.10:8080 3. 4. 手机进入设置 -> Wi-Fi -> 找到HTTP代理选项&#xff0c;选择手动&#xff0c;192.168.31.10:8080 …

gitlab注册无中国区电话验证问题

众所周知gitlab对中国区不友好&#xff0c;无法直接注册&#xff0c;页面无法选择86的手机号进行验证码发送。 Google上众多的方案是修改dom&#xff0c;而且时间大约是21年以前。 修改dom&#xff0c;对于现在的VUE、React框架来说是没有用的&#xff0c;所以不用尝试。 直接看…