本帖最后由 lyyinhe_xi 于 2023-3-4 19:28 编辑

         APM32E103ZET6 EVAL评估板是APM32E1系列增强型MCU的完整演示和开发平台,它带有一颗基于Arm® Cortex®-M3内核的32位MCU(APM32E103ZET6),工作主频120MHz、Flash 512KB、SRAM 128KB;可提供一个2.4英寸TFT LCD,像素240x320px;存储方面支持EEPROM、SPI FLASH、SD Card以及SDRAM,满足用户更多开发需求;内置2*CAN、1*USB、RTC、ADC等外设资料,并支持USB和CAN同时使用。
       360截图16241229368032.jpg
      作为一颗Cortex®-M3内核的MCU,想来应该和STM32F1系列的有较大的通用性,程序的例程应该一搜一大把,然而仅仅是移值DHT11的驱动就颇费了一番折腾。下面就折腾过程中的一些经验教训做一番总结,期待读过此文的人能少绕些圈,能快速上手。首先从厂家提供的SDK例程开始熟悉开发环境和工程组件结构。


      SDK结构.jpg
        解开SDK包可以看到如上图的目录结构,Readme文件里有关于SDK目录结构的介绍。相关的资源和外设程序构件在Boards和Libraries目录下,板子程序的实例在Examples目录下。进入Examples\ADC\ADC_Potentiometer\Project\MDK,即可打开第一个例程工程。打开后会提示找不到APM32E1硬件设备,可以通过Keil的库管理工具直接下载安装。
         360软件小助手截图20230304142338.jpg
         库安装后就没有报错了,可以直接试着编译,暂时不能通过,仔细看是编译器版本不合。打开工程配置Target选项卡右上角Arm Compiler另选一个编译器版本就解决了,合着就是默认的这个编译器版本是编译通不过的。顺便更改晶振频率为8MHz,Output选项卡下勾选输出HEX文件。
          360软件小助手截图20230304143206.jpg
       修改编译器后就顺利通过编译了,接下来就得把编译好的程序下载到开发板了。首先试了FlyMcu,开发板Boot0置位后,可以连接、识别为STM32F0x,可以下载程序,显示为下载成功,但实际板载程序并未改变,串口下载模式实验失败。
       下载程序成功.jpg
      试了Jlink V8,可能是固件版本原因下载也不成功。最后找出了看家宝贝,9.9的PWLINK2连接、下载、调试均成功。
       微信图片_20230304151201.jpg
      Keil5相关设置:Debug选先卡右上角选CMSIS-DAP,用于程序下载。Utilities选项卡左上角sitting内Debug选项卡左上角选CMSIS-DAP,用于调试连接,此项须先连上PWLINK才有得选。
       360软件小助手截图20230304151855.jpg
      例程在板载按钮和串口输出条件下可以很容易获得结果检验,需要用USB线连接板载串口模块,运行串口调试工具接收程序输出。下面就是I2C EEROM读写例程的运行效果。
       图片1.jpg
      下面开始说道正题,移值DHT11温湿度采集模块驱动的移植。
         微信图片_20230304153847.jpg
       DHT11 是一款湿温度一体化的数字传感器。该传感器包括一个电阻式测湿元件和一个 NTC测温元件,并与一个高性能 8 位单片机相连接。通过单片机等微处理器简单的电路连接就能够实时的采集本地湿度和温度。DHT11 与单片机之间能采用简单的单总线进行通信,仅仅需要一个 I/O 口。
       360软件小助手截图20230304154507.jpg
       我们移植一个STM32工程中的DHT11程序到本开发板。在Boards\Board_APM32E103_EVAL\inc下复制bsp_dht11.h,在Boards\Board_APM32E103_EVAL\src下复制bsp_dht11.c,为保持一致性已更名。然后在Examples目录下复制一个ADC目录副本,更名为DHT11,相应更改工程文件名并打开main.c文件编辑调用和串口输出程序。
       添加包含文件
  1. #include "bsp_dht11.h"

       添加变量定义:
  1. u8 rec_data[2];

       在main主程序的while (1)过程内添加如下程序:
  1. if(DHT11_Init()==0)
  2.                                 {
  3.                                                 if(DHT11_ReadData(rec_data)==0)
  4.                                                 {
  5.                                                         printf("温度:%d℃\r\n",rec_data[1]);
  6.               printf("湿度:%d\r\n",rec_data[0]);
  7.                                                 }else{
  8.                                                         printf("DHT11 read error!\r\n");
  9.                                                 }
  10.                                 }else{
  11.                                         printf("DHT11 Init error!\r\n");
  12.                                 }
      直接编译,一下子就显示十多处错误,不能完成编译。
       360软件小助手截图20230304162219.jpg
      多是显示未定义标签类,原来定义的函数名不一样,逐个对应修改后错误逐一消除,后来编译通过了。下面展示修改内容,有错误标示的做了注释,下方输入相应的修改内容,部分多处同样修改,不重复展示。
      bsp_dht11.h文件内修改:
  1. // #include "sys.h"
  2. // #include "delay.h"
  3. #include "Board.h"
  4. #include "bsp_delay.h"
  5. #include "apm32e10x_gpio.h"
  6. #include <stdio.h>
  7. // #define DHT11PORT        GPIOA        //定义IO接口
  8. // #define DHT11_IO        GPIO_Pin_15        //定义IO接口
  9. #define DHT11PORT        GPIOC        //定义IO接口
  10. #define DHT11_IO        GPIO_PIN_7        //修改IO口为PC7
       bsp_dht11.c文件修改内容:
       1 . 配置端口为输出、输出两个子函数
  1. void DHT11_IO_OUT (void){ //端口变为输出
  2. //GPIO_InitTypeDef  GPIO_InitStructure;         
  3. //    GPIO_InitStructure.GPIO_Pin = DHT11_IO; //选择端口号(0~15或all)                        
  4. //    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //选择IO接口工作方式      
  5. //    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz)  
  6.             GPIO_Config_T gpioConfig;
  7.                         gpioConfig.pin = DHT11_IO;
  8.                         gpioConfig.mode = GPIO_MODE_OUT_PP;  //!< Alternate function output Push-pull
  9.             GPIO_Config(GPIOC, &gpioConfig);
  10.   
  11. //GPIO_Init(DHT11PORT, &GPIO_InitStructure);
  12.         GPIO_Config(DHT11PORT, &gpioConfig);
  13. }
  14. void DHT11_IO_IN (void){ //端口变为输入
  15. //        GPIO_InitTypeDef  GPIO_InitStructure;         
  16. //    GPIO_InitStructure.GPIO_Pin = DHT11_IO; //选择端口号(0~15或all)                        
  17. //    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择IO接口工作方式  
  18.                   GPIO_Config_T gpioConfig;
  19.                         gpioConfig.pin = DHT11_IO;
  20.                         gpioConfig.mode = GPIO_MODE_IN_PU;  //!< Input with pull-up
  21.             GPIO_Config(GPIOC, &gpioConfig);
  22.         //GPIO_Init(DHT11PORT, &GPIO_InitStructure);
  23.         GPIO_Config(DHT11PORT, &gpioConfig);
  24. }
      2.  DHT复位启动子函数
  1. void DHT11_RST (void){ //DHT11端口复位,发出起始信号(IO发送)
  2.         DHT11_IO_OUT();
  3.         //GPIO_ResetBits(DHT11PORT,DHT11_IO); //        
  4.         GPIO_ResetBit(DHT11PORT,DHT11_IO);
  5.         //delay_ms(20); //拉低至少18ms        
  6.   APM_EVAL_DelayMs(20);
  7.         //GPIO_SetBits(DHT11PORT,DHT11_IO); //        
  8.         GPIO_ResetBit(DHT11PORT,DHT11_IO);
  9.         //delay_us(30); //主机拉高20~40us
  10.         APM_EVAL_DelayUs(30);
  11. }
       3.  应答检查子程序
  1. u8 Dht11_Check(void){ //等待DHT11回应,返回1:未检测到DHT11,返回0:成功(IO接收)           
  2.     u8 retry=0;
  3.     DHT11_IO_IN();//IO到输入状态         
  4.    // while (GPIO_ReadInputDataBit(DHT11PORT,DHT11_IO)&&retry<100){//DHT11会拉低40~80us
  5.           while (GPIO_ReadInputBit(DHT11PORT,DHT11_IO)&&retry<100){
  6.         retry++;
  7.         //delay_us(1);
  8.                 APM_EVAL_DelayUs(1);
  9.     }         
  10.     if(retry>=100)return 1; else retry=0;
  11.     //while (!GPIO_ReadInputDataBit(DHT11PORT,DHT11_IO)&&retry<100){//DHT11拉低后会再次拉高40~80us
  12.                 while (!GPIO_ReadInputBit(DHT11PORT,DHT11_IO)&&retry<100){
  13.         retry++;
  14.         //delay_us(1);
  15.                         APM_EVAL_DelayUs(1);
  16.     }
  17.     if(retry>=100)return 1;            
  18.     return 0;
  19. }
      4.  单个位读取子程序
  1. u8 Dht11_ReadBit(void){ //从DHT11读取一个位 返回值:1/0
  2.     u8 retry=0;
  3.     //while(GPIO_ReadInputDataBit(DHT11PORT,DHT11_IO)&&retry<100){//等待变为低电平
  4.         while(GPIO_ReadInputBit(DHT11PORT,DHT11_IO)&&retry<100){
  5.         retry++;
  6.         //delay_us(1);
  7.                         APM_EVAL_DelayUs(1);
  8.     }
  9.     retry=0;
  10.     //while(!GPIO_ReadInputDataBit(DHT11PORT,DHT11_IO)&&retry<100){//等待变高电平
  11.                 while(!GPIO_ReadInputBit(DHT11PORT,DHT11_IO)&&retry<100){
  12.         retry++;
  13.         //delay_us(1);
  14.                         APM_EVAL_DelayUs(1);
  15.     }
  16.     //delay_us(40);//等待40us        //用于判断高低电平,即数据1或0
  17.                 APM_EVAL_DelayUs(40);
  18.     //if(GPIO_ReadInputDataBit(DHT11PORT,DHT11_IO))return 1; else return 0;                 
  19.                 if(GPIO_ReadInputBit(DHT11PORT,DHT11_IO))return 1; else return 0;
  20. }
      5.  DHT初始化子程序
  1. u8 DHT11_Init (void){
  2.         //RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE, ENABLE); //APB2外设时钟使能      
  3.         //GPIO_Reset(GPIOA );
  4.             /* Enable the BUTTON Clock */
  5.    // RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA | RCM_APB2_PERIPH_GPIOB | RCM_APB2_PERIPH_GPIOC | RCM_APB2_PERIPH_GPIOD | RCM_APB2_PERIPH_GPIOE );
  6.         RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOC|RCM_APB2_PERIPH_AFIO);
  7.         DHT11_RST();
  8.         return Dht11_Check();
  9. }
          此处没有找到对应的APB2外设时钟使能同样的库指令,不知这样能否正常运行程序。
         经过以上修改,程序编译通过。
          360软件小助手截图20230304165632.jpg    
         串口波特率115200,运行的结果:
          360软件小助手截图20230304170151.jpg
          似乎不能获得DHT11应答,还在进一步调试捉虫中。
         最后补充一个DHT11单线数据传输时序定义。单线就是分时用来输入输出,每次需要读取数据时,MCU发出一个触发信号,DHT则在接受触发信号后连续发送40位的数据信息。
         360软件小助手截图20230304185935.jpg
          上图中粗黑部分就是MCU发出的触发信号,淡色部分就是DHT回送的数据信号。
         360软件小助手截图20230304190011.jpg
          DHT11 上电后要等待 1S 以越过不稳定状态在此期间不能发送任何指令。要读数时MCU输出低电平,且低电平保持时间不能小于 18ms(最大不得超过 30ms),随后进入上拉输入等待接收DHT回送的数据信号
          360软件小助手截图20230304190059.jpg
          DHT则在接受触发信号后连续发送40位的数据信息,因为单线,数据0和1也是通过高低电平持续的时长不同来区分的。位数据“0”的格式为: 54 微秒的低电平和 23-27 微秒的高电平,位数据“1”的格式为: 54 微秒的低电平加 68-74微秒的高电平。
          那么,前面移植的程序没有触发DHT11回送信号,问题是不是出在电平持续时间这里呢?