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页`
`1)HAL_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__)` `//清除串口空闲中断`
`5)HAL_UART_DMAStop(UART_HandleTypeDef * huart)` `//关闭串口DMA通道`
`6)设定的传输长度-剩余传输数量(DMA_CNDTRx)=实际长度`
`7)HAL_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的回应`
`}`

`
相关推荐
代码敲不对.2 小时前
江科大笔记—软件安装
笔记·stm32·单片机·嵌入式硬件
&AtTiTuDe;4 小时前
如何使用IIC外设(硬件IIC)
经验分享·笔记·stm32·单片机·嵌入式硬件·硬件工程
LightningJie4 小时前
STM32(十五):I2C通信
stm32·单片机·嵌入式硬件
辰哥单片机设计4 小时前
4×4矩阵键盘详解(STM32)
stm32·单片机·嵌入式硬件·矩阵·计算机外设·传感器
xuhc_zd4 小时前
STM32外设-0.96寸OLED显示屏
stm32·单片机
安科瑞刘鸿鹏5 小时前
分布式光伏发电系统如何确保电能质量达到并网要求?
服务器·网络·分布式·嵌入式硬件·能源
侥幸哥f5 小时前
GD32F103单片机-EXTI外部中断
单片机·gd32·exti
HeiLongMada6 小时前
从边缘设备到云端平台,合宙DTU&RTU打造无缝物联网解决方案
嵌入式硬件·物联网·硬件工程·pcb工艺
QQ19284999067 小时前
基于STM32无刷直流电机调速蓝牙APP无线监测控制系统
stm32·嵌入式硬件·mongodb
OH五星上将8 小时前
OpenHarmony(鸿蒙南向开发)——标准系统方案之瑞芯微RK3568移植案例(下)
linux·驱动开发·嵌入式硬件·harmonyos·openharmony·鸿蒙开发·系统移植