Bootstrap

STM32完全学习——DS18B20移植到STM32F407ZGT6

一、关于GPIO的初始化

void GPIOG_init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);   //别忘记打开时钟
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;  
	GPIO_InitStruct.GPIO_Speed = GPIO_High_Speed;
	GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;   //一定要配置成开漏输出,这样才能实现双向IO
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;     //一定要配置成上拉和开漏输出一起配合使用才有双向IO的效果
	GPIO_Init(GPIOG, &GPIO_InitStruct);
}

二、DS18B20的相关函数函数

uint8_t ds18b20_init(void)
{
	uint8_t i = 0;
	
	DS18B20_BUS_OUT = 1;    //将数据线拉高方便产生下降沿
	delay_us(1);
	DS18B20_BUS_OUT = 0;    //产生复位脉冲
	delay_us(500);          //最短为480微秒的低电平信号
	DS18B20_BUS_OUT = 1;    //释放掉总线并进入接收状态
	delay_us(1);
	while (DS18B20_BUS_IN) //循环检测ds18b20发送过来的存在信号
	{
		i++;
		delay_us(20);      //每20微秒检测一次
		if (i > 7)
		{
			return 1;       //如果等了140微秒还没有就返回1,表示失败
		}
	}
	delay_us(500);       //如果没有失败,就延时至少480微秒,等待ds18b20将总线释放
	return 0;
}

//给ds18b20写入数据
void ds18b20_write(uint8_t cmd)
{
	uint8_t i = 0, temp = 0;
	for (i=0; i<8; i++)
	{
		DS18B20_BUS_OUT = 0;       //首先将总线拉低,产生写时序
		delay_us(1);               //延时一个微秒
		DS18B20_BUS_OUT = 1;       //释放总线
		temp = (cmd & 0x01);   
		DS18B20_BUS_OUT = temp;    //发送0或者1
		delay_us(60);           //凑够至少60微秒的时间
		DS18B20_BUS_OUT = 1;       //释放总线
		cmd >>= 1;             //连续两个写周期间隙应该大于1us
		delay_us(1);           //和这些低级传感器通信的时候一定要注意延时的问题
	}
}

//从ds18b20里面读取数据
uint8_t ds18b20_read(void)
{
	uint8_t date = 0, i = 0, temp = 0;
	for (i=0; i<8; i++)
	{
		DS18B20_BUS_OUT = 0;            //拉低总线产生读时序的起始信号
		delay_us(1);                    //产生1us的延时
		DS18B20_BUS_OUT = 1;            //释放掉总线
		delay_us(6);                   //延时6us 这里也可以是其他的延时只要保证15us以内
		temp = DS18B20_BUS_IN;         //采集ds18b20上面的数据
		date = (date | (temp << i));//采集到的数据从低位到高位一次放好
		delay_us(60);                //等待ds18b20释放总线后,准备下一次的就收
	}
	return date;
}

//发送检测温度的命令
void ds18b20_change_temperature(void)
{
	uint8_t dat = 0;
	ds18b20_init();        //首先进行初始化
	ds18b20_write(0xcc);   //发送ROM指令
	ds18b20_write(0x44);   //发送温度转换指令
	while (dat != 0xff)    //动态检测DS18B20温度转换情况,转换完毕才开始读取温度数据
	{
		dat = ds18b20_read();
	}
//	delay1s();             //等待温度转换完毕
}


//读取转换完成的温度
//注意这里温度转换过程中将温度结果放大了1000倍
float ds18b20_read_temperatur(void)
{
	uint32_t temp = 0;
	float resout = 0;      //存储温度结果
	uint8_t temp1 = 0, temp2 = 0;
	ds18b20_init();          //首先进行初始化
	ds18b20_write(0xcc);     //发送ROM指令
	ds18b20_write(0xbe);     //读取暂存器
	temp1 = ds18b20_read();	 //读取暂存器第0个字节    温度的低8位   
	temp2 = ds18b20_read();  //读取暂存器第1个字节    温度的高8位
	temp = ((temp2 << 8) | temp1);	
	resout = temp * 0.0625;      //将温度进行转换
	return resout;
}

下面对上面代码的细节做一些解释,首先我使用了STM32里面的bitband,也就是位带访问的方式来操作GPIO的端口。下面是我的biband.h里面的代码,下面这个代码是我对普中官方的例程里面做了一些修改。

#ifndef __BITBAND_H__
#define __BITBAND_H__

#include "stm32f4xx.h"

//位带操作,实现类似51的IO口控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).M4同M3类似,只是寄存器地址变了.
//IO口操作宏定义
#define BITBAND(addr, bitnum)    (0x42000000 + ((addr & 0xFFFFF) * 32)+(bitnum * 4)) 
#define MEM_ADDR(addr)           *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 


//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE + 0x14) //0x40020014
#define GPIOB_ODR_Addr    (GPIOB_BASE + 0x14) //0x40020414 
#define GPIOC_ODR_Addr    (GPIOC_BASE + 0x14) //0x40020814 
#define GPIOD_ODR_Addr    (GPIOD_BASE + 0x14) //0x40020C14 
#define GPIOE_ODR_Addr    (GPIOE_BASE + 0x14) //0x40021014 
#define GPIOF_ODR_Addr    (GPIOF_BASE + 0x14) //0x40021414    
#define GPIOG_ODR_Addr    (GPIOG_BASE + 0x14) //0x40021814   
#define GPIOH_ODR_Addr    (GPIOH_BASE + 0x14) //0x40021C14    
#define GPIOI_ODR_Addr    (GPIOI_BASE + 0x14) //0x40022014     

#define GPIOA_IDR_Addr    (GPIOA_BASE + 0x10) //0x40020010 
#define GPIOB_IDR_Addr    (GPIOB_BASE + 0x10) //0x40020410 
#define GPIOC_IDR_Addr    (GPIOC_BASE + 0x10) //0x40020810 
#define GPIOD_IDR_Addr    (GPIOD_BASE + 0x10) //0x40020C10 
#define GPIOE_IDR_Addr    (GPIOE_BASE + 0x10) //0x40021010 
#define GPIOF_IDR_Addr    (GPIOF_BASE + 0x10) //0x40021410 
#define GPIOG_IDR_Addr    (GPIOG_BASE + 0x10) //0x40021810 
#define GPIOH_IDR_Addr    (GPIOH_BASE + 0x10) //0x40021C10 
#define GPIOI_IDR_Addr    (GPIOI_BASE + 0x10) //0x40022010 
 
//IO口操作,只对单一的IO口
//确保n的值小于16
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入

#define PHout(n)   BIT_ADDR(GPIOH_ODR_Addr,n)  //输出 
#define PHin(n)    BIT_ADDR(GPIOH_IDR_Addr,n)  //输入

#define PIout(n)   BIT_ADDR(GPIOI_ODR_Addr,n)  //输出 
#define PIin(n)    BIT_ADDR(GPIOI_IDR_Addr,n)  //输入


#endif


怎么使用上面这些代码呢

#define DS18B20_BUS_OUT PGout(9)
#define DS18B20_BUS_IN  PGin(9)

看上面这个代码你在看我写的DS18B20的函数,你就会发现当你想要输出的时候,就直接给PGout()直接赋值就行了,括号里面填的是具体哪个引脚。如果你想获取输入得值,那么你只需要用temp = PGin(9)这样引脚的高低电平就被存储到了这个变量里面。再就是我这里的延时使用的是systick实现精确延时。想了解的可以看我的另外一个博客。

注意在mian函数里面一定要完成两个初始化一个是GPIO一个是DS18B20。


int main(void)
{
	float temp = 0;
	GPIOG_init();
	ds18b20_init();
    while(1)
    {
		ds18b20_change_temperature();
		temp = ds18b20_read_temperatur();
		printf("temp = %.2f\n", temp);
		delay_ms(100);
    }
}

三、printf的重定向

为了方便输出我这里将printf函数做了重定向,前提是你必须对串口做了相关的初始化,不然printf函数也不知道要发到哪里去呀


int fputc(int ch, FILE* str) 
{
	FlagStatus temp;
	while(temp != SET)           //先阻塞时判断串口是否发送完毕
	{
		temp = USART_GetFlagStatus(USART1,USART_FLAG_TC);
	}
	USART_SendData(USART1, (uint8_t)ch);
    return ch;
}

注意一定要勾选上面这个,而且还有包含头文件#include <stdio.h>

;