【试用】 国产MCU神作GD207ZE—ADC+DHT11+UART

[复制链接]
2633|5
 楼主| jjlink 发表于 2016-4-9 11:22 | 显示全部楼层 |阅读模式
     首先,再次感谢21IC给了我关于GDF207ZE的试用机会,关于GD32F207的详细介绍,详情可以参照我的上一个帖子:
   【试用】 国产MCU神作GD207ZE——开箱篇!
https://bbs.21ic.com/forum.php?mo ... amp;fromuid=1428042
    好了,废话不多说,接下来开始我们关于GD32F207的第一程序的开发吧。

    参考了关于这颗芯片的相关手册,发现跟STM32的很多架构,寄存器都很通用,因此,希望对于以前关于stm32的例程就很容易可以移植了,这里,我借用了以前写过的驱动,在GD板子上进行一些简易的移植,这里,主要用的是DHT11跟ADC模块,然后主要通过串口输出信息,以看到相关的测量结果~
   
     简单描述一下实现过程:
1.数字温湿度传感器DHT11采用单总线输出数据,单次数据包共5Byte(40bit),包括先发送高位,数据格式为:湿度整数+湿度小数+温度整数+温度小数+校验和,各占1 Byte,校验和为前4个Byte相加,由此可计算出温湿度的数值。
DHT11通讯过程的时序图如下图所示,空闲状态时总线为高电平,MCU发送开始信号,拉低总线等待DHT11响应(至少18ms),之后拉高总线20~40μs;DHT11接收到开始信号,在其结束后发送80μs的低电平响应信号,之后拉高总线40~50μs,再拉低40~50μs,接着输出数据;主机读取到响应信号后开始接收数据;一次数据传输完成,DHT11拉低总线50μs,之后总线进入空闲状态。    相关的操作手册在DHT11的数据手册里面已经详细提及到了,感兴趣的朋友可以去下载来看看,这里不再多说了!
    下面请上我们的驱动代码:
  1. #include "dht11.h"

  2. void DHT11_IO_IN(void)
  3. {
  4.         GPIO_InitPara GPIO_InitStructure;
  5.         RCC_APB2PeriphClock_Enable(RCC_APB2PERIPH_GPIOG,ENABLE);         //使能PF端口时钟
  6.         GPIO_InitStructure.GPIO_Pin = DHT11_Pin;                                 //端口配置
  7.         GPIO_InitStructure.GPIO_Mode = GPIO_MODE_IPU;
  8.         GPIO_Init(DHT11_GPIO_Port, &GPIO_InitStructure);                                 //初始化IO口
  9. }

  10. void DHT11_IO_OUT(void)
  11. {
  12.         GPIO_InitPara GPIO_InitStructure;
  13.         RCC_APB2PeriphClock_Enable(RCC_APB2PERIPH_GPIOG,ENABLE);         //使能PF端口时钟
  14.         GPIO_InitStructure.GPIO_Pin = DHT11_Pin;                                 //端口配置
  15.         GPIO_InitStructure.GPIO_Mode = GPIO_MODE_OUT_PP;                  //推挽输出
  16.         GPIO_InitStructure.GPIO_Speed = GPIO_SPEED_10MHZ;
  17.         GPIO_Init(DHT11_GPIO_Port, &GPIO_InitStructure);                                 //初始化IO口
  18. }

  19. //复位DHT11
  20. void DHT11_Rst(void)
  21. {                 
  22.         DHT11_IO_OUT();
  23.         CLR_DHT11();         //拉低DQ
  24.   delay_ms(20); //拉低至少18ms
  25.   SET_DHT11();         //DQ=1
  26.         delay_us(30); //主机拉高20~40us
  27. }
  28. //等待DHT11的回应
  29. //返回1:未检测到DHT11的存在
  30. //返回0:存在
  31. uint8_t DHT11_Check(void)
  32. {   
  33.         uint8_t retry=0;
  34.         DHT11_IO_IN();//SET INPUT         
  35.   while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~80us
  36.         {
  37.                 retry++;
  38.                 delay_us(1);
  39.         };         
  40.         if(retry>=100)return 1;
  41.         else retry=0;
  42.   while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~80us
  43.         {
  44.                 retry++;
  45.                 delay_us(1);
  46.         };
  47.         if(retry>=100)return 1;            
  48.         return 0;
  49. }
  50. //从DHT11读取一个位
  51. //返回值:1/0
  52. uint8_t DHT11_Read_Bit(void)                          
  53. {
  54.         uint8_t retry=0;
  55.         while(DHT11_DQ_IN&&retry<100)//等待变为低电平
  56.         {
  57.                 retry++;
  58.                 delay_us(1);
  59.         }
  60.         retry=0;
  61.         while(!DHT11_DQ_IN&&retry<100)//等待变高电平
  62.         {
  63.                 retry++;
  64.                 delay_us(1);
  65.         }
  66.         delay_us(40);//等待40us
  67.         if(DHT11_DQ_IN)return 1;
  68.         else return 0;                  
  69. }
  70. //从DHT11读取一个字节
  71. //返回值:读到的数据
  72. uint8_t DHT11_Read_Byte(void)   
  73. {        
  74.   uint8_t i,dat;
  75.   dat=0;
  76.         for (i=0;i<8;i++)
  77.         {
  78.            dat<<=1;
  79.           dat|=DHT11_Read_Bit();
  80.   }                                                    
  81.     return dat;
  82. }
  83. //从DHT11读取一次数据
  84. //temp:温度值(范围:0~50°)
  85. //humi:湿度值(范围:20%~90%)
  86. //返回值:0,正常;1,读取失败
  87. uint8_t DHT11_Read_Data(uint8_t *temp,uint8_t *humi)   
  88. {        
  89.         uint8_t buf[5];
  90.         uint8_t i;
  91.         DHT11_Rst();
  92.         if(DHT11_Check()==0)
  93.         {
  94.                 for(i=0;i<5;i++)//读取40位数据
  95.                 {
  96.                         buf[i]=DHT11_Read_Byte();
  97.                 }
  98.                 if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
  99.                 {
  100.                         *humi=buf[0];
  101.                         *temp=buf[2];
  102.                 }
  103.         }else return 1;
  104.         return 0;            
  105. }
  106. //初始化DHT11的IO口 DQ 同时检测DHT11的存在
  107. //返回1:不存在
  108. //返回0:存在             
  109. uint8_t DHT11_Init(void)
  110. {
  111.         DHT11_IO_OUT();
  112.         SET_DHT11();
  113.         DHT11_Rst();  //复位DHT11
  114.         return DHT11_Check();//等待DHT11的回应
  115. }

相关的驱动相信大家也很熟悉啦,这里是引用了原子哥的驱动来移植的,感谢原子哥~
同时,还有ADC 部分的驱动代码:
  1. #include "adc.h"
  2. #include "delay.h"

  3. //初始化ADC                                                                                                                          
  4. void  Adc_Init(void)
  5. {        
  6.         ADC_InitPara ADC_InitStructure;
  7.         GPIO_InitPara GPIO_InitStructure;

  8.         RCC_APB2PeriphClock_Enable(RCC_APB2PERIPH_GPIOA | RCC_APB2PERIPH_ADC1, ENABLE );          //使能ADC1通道时钟
  9.         RCC_ADCCLKConfig(RCC_ADCCLK_APB2_DIV6);   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M

  10.         //PA1、PA2作为模拟通道输入引脚                        
  11.         GPIO_InitStructure.GPIO_Pin = GPIO_PIN_1 | GPIO_PIN_2;
  12.         GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AIN;                //模拟输入引脚
  13.         GPIO_Init(GPIOA, &GPIO_InitStructure);       

  14.         ADC_InitStructure.ADC_Mode = ADC_MODE_INDEPENDENT;        //ADC工作模式:ADC1工作在独立模式
  15.         ADC_InitStructure.ADC_Mode_Scan = DISABLE;        //模数转换工作在单通道模式
  16.         ADC_InitStructure.ADC_Mode_Continuous = DISABLE;        //模数转换工作在单次转换模式
  17.         ADC_InitStructure.ADC_Trig_External = ADC_EXTERNAL_TRIGGER_MODE_NONE;        //转换由软件而不是外部触发启动
  18.         ADC_InitStructure.ADC_Data_Align = ADC_DATAALIGN_RIGHT;        //ADC数据右对齐
  19.         ADC_InitStructure.ADC_Channel_Number = 2;        //顺序进行规则转换的ADC通道的数目
  20.         ADC_Init(ADC1, &ADC_InitStructure);        //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器   
  21.        
  22.         ADC_Enable(ADC1,ENABLE);
  23.         ADC_Calibration(ADC1);

  24. }                                  
  25. //获得ADC值
  26. //ch:通道值 0~3
  27. uint16_t Get_Adc(uint8_t ch)   
  28. {
  29.           //设置指定ADC的规则组通道,一个序列,采样时间
  30.         ADC_RegularChannel_Config(ADC1,ch,1,ADC_SAMPLETIME_239POINT5);        //ADC1,ADC通道,采样时间为239.5周期                                      
  31.   
  32.         ADC_SoftwareStartConv_Enable(ADC1,ENABLE);                //使能指定的ADC1的软件转换启动功能       
  33.          
  34.         while(ADC_GetBitState(ADC1,ADC_FLAG_EOC)!=SET);//等待转换结束

  35.         return ADC_GetConversionValue(ADC1);        //返回最近一次ADC1规则组的转换结果
  36. }

  37. float Get_Adc_Average(uint8_t ch,uint8_t times)
  38. {
  39.         uint32_t temp_val=0;
  40.         uint8_t t;
  41.         for(t=0;t<times;t++)
  42.         {
  43.                 temp_val+=Get_Adc(ch);
  44.                 delay_ms(5);
  45.         }
  46.         return (float)temp_val/times* (3.3 / 4096.0);
  47. }
分别读取ADC1的ADC_IN1和ADC_IN2的转换值,取3次的平均值后转换为0-3.3V电压值发送到PC。
USART发送程序采用colibri_bsp_uart.c中的EvbUart1Printf(char* fmt, ...)函数,该函数实现了printf()的功能,按照printf()的格式调用即可。
硬件接线如下:
                          PG8:DHT11 DATA
                          PA1:电位器1
                          PA2:电位器2
                          PB6:USART1 TX

其次,在对于DHT11的操作中,最为重要的还是延时函数的精确性:
其中用到的延时函数采用cortex-M3内核中的跟踪组件DWT的时钟周期计数CYCCNT实现:
  1. #include "delay.h"

  2. #define  DWT_CR      *(volatile u32 *)0xE0001000
  3. #define  DWT_CYCCNT  *(volatile u32 *)0xE0001004
  4. #define  DEM_CR      *(volatile u32 *)0xE000EDFC
  5. #define  DEM_CR_TRCENA                   (1 << 24)
  6. #define  DWT_CR_CYCCNTENA                (1 <<  0)
  7. static uint32_t cpuclkfeq;

  8. void delay_init(uint32_t clk)
  9. {
  10.     cpuclkfeq = clk;
  11.     DEM_CR |= DEM_CR_TRCENA;
  12. //    DWT_CYCCNT      = 0u;
  13.     DWT_CR |= DWT_CR_CYCCNTENA;
  14. }
  15. void delay_us(uint32_t us)
  16. {
  17.     uint32_t temp;
  18.     temp= DWT_CYCCNT;
  19.     us *=  SystemCoreClock/1000000;
  20.     while((uint32_t)( DWT_CYCCNT - temp)< us);
  21. }
  22. //void delay_us(uint32_t value)
  23. //{
  24. //        uint32_t i;
  25. //        i = value * 15;
  26. //        while(i--);
  27. //}

  28. void delay_ms(uint32_t ms)
  29. {
  30.         uint32_t temp;
  31.         temp= DWT_CYCCNT;
  32.         ms *=  SystemCoreClock/1000;
  33.         while((uint32_t)( DWT_CYCCNT - temp)< ms);
  34. }

综上,将代码工程编译通后,通过GD-LINK连接到电脑进行下载:
运行结果如下:
QQ截图20160409112057.jpg

下面再附上代码文件,请大家轻喷~

GD32F207.zip (3.97 MB, 下载次数: 30)





 楼主| jjlink 发表于 2016-4-9 11:23 | 显示全部楼层
前排占座~
comeon201208 发表于 2016-4-17 13:00 | 显示全部楼层
这个还是采集温湿度及电压的程序设计的。
 楼主| jjlink 发表于 2016-4-17 19:17 | 显示全部楼层
comeon201208 发表于 2016-4-17 13:00
这个还是采集温湿度及电压的程序设计的。

嗯。就是温湿度采集加上ADC转换的。有兴趣欢迎下载代码来看看
shenmu2012 发表于 2016-4-18 21:01 | 显示全部楼层
数字温湿度传感器DHT11采用单总线输出数据,单次数据包共5Byte(40bit),包括先发送高位,数据格式为:湿度整数+湿度小数+温度整数+温度小数+校验和,各占1 Byte,校验和为前4个Byte相加,由此可计算出温湿度的数值。
这个是很费心了
LXBYUN 发表于 2022-4-1 11:29 | 显示全部楼层
你好!这个版本的LIB库,定时器能否正常工作?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

7

主题

73

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部