首先,再次感谢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的数据手册里面已经详细提及到了,感兴趣的朋友可以去下载来看看,这里不再多说了!
下面请上我们的驱动代码:
#include "dht11.h"
void DHT11_IO_IN(void)
{
GPIO_InitPara GPIO_InitStructure;
RCC_APB2PeriphClock_Enable(RCC_APB2PERIPH_GPIOG,ENABLE); //使能PF端口时钟
GPIO_InitStructure.GPIO_Pin = DHT11_Pin; //端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_MODE_IPU;
GPIO_Init(DHT11_GPIO_Port, &GPIO_InitStructure); //初始化IO口
}
void DHT11_IO_OUT(void)
{
GPIO_InitPara GPIO_InitStructure;
RCC_APB2PeriphClock_Enable(RCC_APB2PERIPH_GPIOG,ENABLE); //使能PF端口时钟
GPIO_InitStructure.GPIO_Pin = DHT11_Pin; //端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_MODE_OUT_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_SPEED_10MHZ;
GPIO_Init(DHT11_GPIO_Port, &GPIO_InitStructure); //初始化IO口
}
//复位DHT11
void DHT11_Rst(void)
{
DHT11_IO_OUT();
CLR_DHT11(); //拉低DQ
delay_ms(20); //拉低至少18ms
SET_DHT11(); //DQ=1
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);
}
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%)
//返回值:0,正常;1,读取失败
uint8_t DHT11_Read_Data(uint8_t *temp,uint8_t *humi)
{
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])
{
*humi=buf[0];
*temp=buf[2];
}
}else return 1;
return 0;
}
//初始化DHT11的IO口 DQ 同时检测DHT11的存在
//返回1:不存在
//返回0:存在
uint8_t DHT11_Init(void)
{
DHT11_IO_OUT();
SET_DHT11();
DHT11_Rst(); //复位DHT11
return DHT11_Check();//等待DHT11的回应
}
相关的驱动相信大家也很熟悉啦,这里是引用了原子哥的驱动来移植的,感谢原子哥~
同时,还有ADC 部分的驱动代码:
#include "adc.h"
#include "delay.h"
//初始化ADC
void Adc_Init(void)
{
ADC_InitPara ADC_InitStructure;
GPIO_InitPara GPIO_InitStructure;
RCC_APB2PeriphClock_Enable(RCC_APB2PERIPH_GPIOA | RCC_APB2PERIPH_ADC1, ENABLE ); //使能ADC1通道时钟
RCC_ADCCLKConfig(RCC_ADCCLK_APB2_DIV6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
//PA1、PA2作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_1 | GPIO_PIN_2;
GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AIN; //模拟输入引脚
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_InitStructure.ADC_Mode = ADC_MODE_INDEPENDENT; //ADC工作模式:ADC1工作在独立模式
ADC_InitStructure.ADC_Mode_Scan = DISABLE; //模数转换工作在单通道模式
ADC_InitStructure.ADC_Mode_Continuous = DISABLE; //模数转换工作在单次转换模式
ADC_InitStructure.ADC_Trig_External = ADC_EXTERNAL_TRIGGER_MODE_NONE; //转换由软件而不是外部触发启动
ADC_InitStructure.ADC_Data_Align = ADC_DATAALIGN_RIGHT; //ADC数据右对齐
ADC_InitStructure.ADC_Channel_Number = 2; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
ADC_Enable(ADC1,ENABLE);
ADC_Calibration(ADC1);
}
//获得ADC值
//ch:通道值 0~3
uint16_t Get_Adc(uint8_t ch)
{
//设置指定ADC的规则组通道,一个序列,采样时间
ADC_RegularChannel_Config(ADC1,ch,1,ADC_SAMPLETIME_239POINT5); //ADC1,ADC通道,采样时间为239.5周期
ADC_SoftwareStartConv_Enable(ADC1,ENABLE); //使能指定的ADC1的软件转换启动功能
while(ADC_GetBitState(ADC1,ADC_FLAG_EOC)!=SET);//等待转换结束
return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
}
float Get_Adc_Average(uint8_t ch,uint8_t times)
{
uint32_t temp_val=0;
uint8_t t;
for(t=0;t<times;t++)
{
temp_val+=Get_Adc(ch);
delay_ms(5);
}
return (float)temp_val/times* (3.3 / 4096.0);
}
分别读取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实现:
#include "delay.h"
#define DWT_CR *(volatile u32 *)0xE0001000
#define DWT_CYCCNT *(volatile u32 *)0xE0001004
#define DEM_CR *(volatile u32 *)0xE000EDFC
#define DEM_CR_TRCENA (1 << 24)
#define DWT_CR_CYCCNTENA (1 << 0)
static uint32_t cpuclkfeq;
void delay_init(uint32_t clk)
{
cpuclkfeq = clk;
DEM_CR |= DEM_CR_TRCENA;
// DWT_CYCCNT = 0u;
DWT_CR |= DWT_CR_CYCCNTENA;
}
void delay_us(uint32_t us)
{
uint32_t temp;
temp= DWT_CYCCNT;
us *= SystemCoreClock/1000000;
while((uint32_t)( DWT_CYCCNT - temp)< us);
}
//void delay_us(uint32_t value)
//{
// uint32_t i;
// i = value * 15;
// while(i--);
//}
void delay_ms(uint32_t ms)
{
uint32_t temp;
temp= DWT_CYCCNT;
ms *= SystemCoreClock/1000;
while((uint32_t)( DWT_CYCCNT - temp)< ms);
}
综上,将代码工程编译通后,通过GD-LINK连接到电脑进行下载:
运行结果如下:
下面再附上代码文件,请大家轻喷~
GD32F207.zip
(3.97 MB)
|