一、关于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>