2019-05-25 20:14:10 wj2020scu 阅读数 922
  • 温度传感器DS18B20-第2季第1部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第1个课程,主要讲解单片机系统中常用的温度传感器DS18B20。本课程的目标是让大家进一步掌握时序的分析和编程实现,学会移植和调试DS18B20的程序,能够读取温度。

    2184 人正在学习 去看看 朱有鹏
  1. 开发准备
    (1)YF-B1流量传感器一个
    (2)51开发板一个

  2. 基础知识
    (1)YF-B1流量传感器只有三根线。即数据线、VCC、GND。数据线输出为占空比为50%的方波。当水流通过水流转子组件时,磁性转子转动并且转速随着流量的变化而成线性变化。霍尔传感器(霍尔元件采样)输出相应的脉冲信号。其流量脉冲特性计算公式为:脉冲f(Hz)=8.1x流量Q(L/min)-3。
    (2)51单片机的中断工作方式。51单片机有定时器T0和T1,他们既有定时又有计数的功能。通过设置相关的特殊功能寄存器就可以启用定时或计数功能。需要注意的是,定时器系统是单片机内部一个独立的硬件部分,CPU一旦设置定时功能,定时器便在晶振的作用下计时,当计数器计满便会产生中断,通知CPU该如何去处理。而作为计数器时,计数脉冲来自相应的外部输入引脚T0(P3.4),T1(P3.5)或者T2(P1.0,52单片机)。

  3. 电路设计
    (1)将霍尔传感器数据线插在P3.4,VCC接在VCC,GND接GND。
    这里插入图片描述
    (2)这是我的开发板数码管的电路,提供参考。
    在这里插入图片描述

  4. 软件设计

#include<reg52.h>
#include<stdio.h>
#define uchar unsigned char   //宏定义
#define uint  unsigned int   //宏定义
sbit we = P2^7; //位定义数码管位选锁存器接口
sbit du = P2^6;  //位定义数码管位选锁存器接口
float frency,Q,F,num;//Q为流量,单位L/min;F为频率,单位HZ
uchar code leddata[]={ 
 
                0x3F,  //"0"
                0x06,  //"1"
                0x5B,  //"2"
                0x4F,  //"3"
                0x66,  //"4"
                0x6D,  //"5"
                0x7D,  //"6"
                0x07,  //"7"
                0x7F,  //"8"
                0x6F,  //"9"
                0x77,  //"A"
                0x7C,  //"B"
                0x39,  //"C"
                0x5E,  //"D"
                0x79,  //"E"
                0x71,  //"F"
                0x76,  //"H"
                0x38,  //"L"
                0x37,  //"n"
                0x3E,  //"u"
                0x73,  //"P"
                0x5C,  //"o"
                0x40,  //"-"
                0x00,  //熄灭
                0x00  //自定义
 
                         };
                 //数码管带小数点显示
uchar code leddatapoint[]={ 
 
                0xBF,  //"0"
                0x86,  //"1"
                0xDB,  //"2"
                0xCF,  //"3"
                0xE6,  //"4"
                0xED,  //"5"
                0xFD,  //"6"
                0x87,  //"7"
                0xFF,  //"8"
                0xEF,  //"9"
                0x00,  //熄灭
                0x00  //自定义
 
                         };
//毫秒级延时函数
void delay(uint z)
{
 uint x,y;
 for(x = z; x > 0; x--)
  for(y = 114; y > 0 ; y--);
}
//四位数码管动态显示函数
void display(float i)
{
 uchar shi, ge,fen,shifen;
 float x,y;
 shi = i / 10; //显示十位
 ge  = (int)i % 10;//显示个位
 x=i*10;
 y=i*100;
 fen = (int)x%10;//分位
 shifen = (int)y%10;//十分位
 
 P0 = 0xff;//清除断码
 we = 1; //打开位选
 P0 = 0xef;//1110 1111  
 we = 0; //关闭位选
 
 du = 1;//打开段选
 P0 = leddata[shi]; 
 du = 0; //关闭段选
 delay(5);//延时5毫秒
 
 P0 = 0xff;//清除断码
 we = 1; //打开位选
 P0 = 0xdf;//1101 1111  
 we = 0; //关闭位选
 
 du = 1;//打开段选
 P0 = leddatapoint[ge]; 
 du = 0; //关闭段选
 delay(5);//延时5毫秒
 
 P0 = 0xff; //清除断码
 we = 1;//打开位选
 P0 = 0xbf;//1011 1111 
 we = 0; //关闭位选
 
du = 1; //打开段选
 P0 = leddata[fen];
 du = 0; //关闭段选
 delay(5);//延时5毫秒

P0 = 0xff; //清除断码
 we = 1;//打开位选
 P0 = 0x7f;//0111 1111 
 we = 0; //关闭位选

du = 1; //打开段选
 P0 = leddata[shifen];
 du = 0; //关闭段选
 delay(5);//延时5毫秒 
}
uint read()//得到计数器0当前脉冲次数函数
{
 uint tl,th1,th2;//读两次高位,两次高位一样说明没有低位进位,读数更加精确。
 uint val;
 while(1)
  {
  th1=TH0;
  tl=TL0;
  th2=TH0;
  if(th1==th2)
   break;
 }
 val=th1*256+tl;
 return val;
}
void main()
{
 TMOD=0x15;//定时器计数器工作方式配置
 TH0=0;
 TL0=0;
 TH1=(65536-45872)/256;
 TL1=(65536-45872)%256;
 EA=1;//开总中断
 ET0=1;//中断允许
 ET1=1;
 TR0=1;//运行控制位
 TR1=1;
 while(1)
 { 
  display(Q);
 }
 
}
void T0_time()interrupt 1
{
 TH0=0;
 TL0=0;
}
void T1_time()interrupt 3
{
 TH1=(65536-45872)/256;//50毫秒
 TL1=(65536-45872)%256;
 num++;
 if(num==20)  //1s更新一次数据,送至数码管显示
 {
  num=0;
  F=read();//每隔1s读一次计数器0,该值则为频率。计算出Q后立马把计数器0清零重新计数。
  if(F>0)
  {
   Q=(F+3)/8.1;//流量传感器经验公式
   TH0=0;
   TL0=0;
  }
  else
  {
   Q=0;//如果不加这句,当F=0,由公式知道Q!=0。
   TH0=0;
   TL0=0;
  }
 } 
}
  1. 测试效果
    在这里插入图片描述写在最后:

上传照片的时候大小超过5M,记录一下调整方法,以免又忘了。
直接使用Windows自带照片编辑工具->点击更多->调整大小。

因为这是之前做比赛的时候剩下的传感器,前两天突然冒出来了,看了看数据手册,发现挺简单的就敲了出来。电路板就没设计了,因为YF-B1的输出太简单了。源码应该没有问题,测试过精度还挺不错的。代码能力还值得提升,有很多冗余的部分,之后有时间优化一下。

2019-09-10 11:45:27 suoxd123 阅读数 151
  • 温度传感器DS18B20-第2季第1部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第1个课程,主要讲解单片机系统中常用的温度传感器DS18B20。本课程的目标是让大家进一步掌握时序的分析和编程实现,学会移植和调试DS18B20的程序,能够读取温度。

    2184 人正在学习 去看看 朱有鹏

版权归如下公司,禁止非授权转载:

  • 北京西普阳光教育科技股份有限公司(https://www.simpleware.com.cn)
  • 维周机器人科技有限公司(http://www.vejoe.com)


【实验目的】

  1. 熟悉编码器的种类及其工作原理;
  2. 熟悉编码器的输出形式,熟悉单片机如何通过编码器的输出得到电机的旋转方向和速率;
  3. 掌握STM32F10xx系列微控制器上编码器的接口配置与数据采集过程;

【实验原理】

编码器(encoder)是指将某一形式的信号或数据(通常特指电机转速)通过采集、编制,转换为可以用于传输、储存的信号形式的设备。编码器按采集方式可以分为接触式编码器和非接触式编码器两种;按工作原理可以分为:光电式、磁电式和触点电刷式编码器。光电式编码器通过采集光通过码盘刻孔产生的脉冲信号的频率进行转换,磁电式编码器通过霍尔元件感应电机转动时产生的磁场变化进行转换,触点电刷式编码器直接采集触点在码盘上的位置进行转换;还可以根据其计数模式分为增量式和绝对式编码器。增量式编码器记录通过采集位移信息产生的脉冲信号,近似于“速率”的概念,绝对式编码器记录位置信息,与过程无关,近似于“路程”的概念。

本实验采用的编码器为增量式编码器,增量式编码器通常有两个输出信号,分别为A相和B相(也有的编码器有A、B、Z三相输出,有编码器只有A相输出)。每一个信号都是由方波构成的脉冲序列,A相和B相之间的相位差为90度。单片机在接收到AB相输入后,可以根据收到相位的先后顺序确定电机的转向,根据脉冲序列的频率确定电机的转速,这种解码方式称为正交解码。值得注意的是,脉冲频率与电机转速成正比关系,而其具体比例系数会和码盘孔数、减速箱齿数等有关,而转速与行驶速率也存在一个比例系数,因此通常在使用中,会根据实地测试结果直接确定一个脉冲频率与行驶速率之间的经验系数。

STM32芯片中集成了强大的定时器模块,其工作状态主要有以下几种模式:输入捕获模式、PWM输入模式、强制输出模式、输出比较模式、PWM模式、单脉冲模式、编码器接口模式、调试模式等。在本实验中需要用到的是定时器模块的编码器接口模式。当定时器工作在编码器模式下时,定时器会采集两个指定的引脚输入信号,然后通过检测输入信号的跳变沿,确定输入脉冲的频率及其相位差,从而计算出电机旋转的方向以及速率。编码器接口模式基本上相当于使用了一个带有方向选择的外部时钟。这意味着计数器只在0到TIMx_ARR寄存器的自动装载值之间连续计数(根据方向,或是0到ARR计数,或是ARR到0计数)。所以在开始计数之前必须配置TIMx_ARR。

在这里插入图片描述
图1 编码器模式下的计数器操作示意图

值得注意的是,仅仅完成跳变沿检测的功能,普通IO口就能办到。而定时器模块的编码器接口模式相比于普通IO口的优势在于可以抵抗一定的噪音,如图1所示,在输入信号有毛刺的情况下,计数器依然能够正常的工作,普通IO口想要办到类似的功能则需要人为添加一系列代码。
程序流程的简要说明图如图2所示:

在这里插入图片描述
图2 程序流程示意图

【实验环境】

硬件设备:

1、双轮自平衡机器人。如图3所示,在平衡车背面中间安装了HC-SR04超声波测距模块。
2、ST-Link下载器(包含USB线与下载线)。如图4所示。
操作系统:
Windows7/8/10,32bit/64bit

在这里插入图片描述
图3 双轮自平衡机器人
在这里插入图片描述在这里插入图片描述
图4 ST-Link下载器与下载线

软件环境:

Keil 5

【实验步骤】

第一步 配置工程环境

(1)打开已经建立好的工程模板,在新建立的工程模板中已经添加五个文件夹,分别命名为USER、HARDWARE、SYSTEM、CORE、FWLib文件夹,如图5所示。其中USER文件夹存放的是主函数,HARDWARE文件夹存放的是本实验对应的硬件设备函数,SYSTEM存放的是本课程所有实验通用的函数,CORE文件夹存放的是启动文件,FWLib文件夹存放的是底层驱动函数。
在这里插入图片描述
图5工程模板对应的文件夹

(2)在HARDWARE文件夹下新建两个文件,分别为encoder.c和encoder.h。分别存放编码器的函数文件和头文件,如图6所示。
在这里插入图片描述
图6 在HARDWARE文件夹下建立encoder.c与encoder.h文件

第二步 编写定时器函数,完成定时器的配置和启用

(3)打开程序中的encoder.c文件,首先将encoder.h文件包含进来。其次对Encoder_Init_TIM2 函数进行编写,TIM2采集的是左侧的码盘返回值,选择要使用的时钟,配置引脚,启用编码器模式并配置参数。

#include "encoder.h"
#include "stm32f10x_gpio.h"
/****************************************************************
函数功能:把TIM2初始化为编码器接口模式
入口参数:无
返回值:无
****************************************************************/
void Encoder_Init_TIM2(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;  
	TIM_ICInitTypeDef TIM_ICInitStructure;  
	GPIO_InitTypeDef GPIO_InitStructure;
//使能定时器4的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//使能PA端口时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;	//端口配置
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);		//初始化GPIOA
	TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
	TIM_TimeBaseStructure.TIM_Prescaler = 0x0; //预分频器
//设定计数器自动重载值
	TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD; 
//选择时钟分频:不分频
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
//TIM向上计数
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, 					TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式
	TIM_ICStructInit(&TIM_ICInitStructure);
	TIM_ICInitStructure.TIM_ICFilter = 10;
	TIM_ICInit(TIM2, &TIM_ICInitStructure);
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);//清除TIM的更新标志位
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	//Reset counter
	TIM_SetCounter(TIM2,0);
	TIM_Cmd(TIM2, ENABLE); 
}

(1) (4)同理,编写Encoder_Init_TIM4函数,TIM4定时器采集的是右侧的编码器返回值。

/****************************************************************
函数功能:把TIM4初始化为编码器接口模式
入口参数:无
返回值:无
****************************************************************/
void Encoder_Init_TIM4(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;  
TIM_ICInitTypeDef TIM_ICInitStructure;  
GPIO_InitTypeDef GPIO_InitStructure;
//使能定时器4的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
//使能PB端口时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;	//端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOB, &GPIO_InitStructure);		//初始化GPIOA
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = 0x0; //预分频器
//设定计数器自动重载值
TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD; 
//选择时钟分频:不分频
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
//TIM向上计数
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12,
TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 10;
TIM_ICInit(TIM4, &TIM_ICInitStructure);
TIM_ClearFlag(TIM4, TIM_FLAG_Update);//清除TIM的更新标志位
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
TIM_SetCounter(TIM4,0);//Reset counter
TIM_Cmd(TIM4, ENABLE); 
}

(5)编写编码器计数器读取函数

/*****************************************************************
函数功能:单位时间读取编码器计数
入口参数:定时器
返回值:速度值
*****************************************************************/
int Read_Encoder(u8 TIMX)
{
	int Encoder_TIM;    
	switch(TIMX)
	{
		case 2:  Encoder_TIM= (short)TIM2 -> CNT;  TIM2 -> CNT=0;break;
		case 4:  Encoder_TIM= (short)TIM4 -> CNT;  TIM4 -> CNT=0;break;	
		default:  Encoder_TIM=0;
	}
	return Encoder_TIM;
}

(2) 打开encoder.h,编写函数声明

#ifndef __ENCODER_H
#define __ENCODER_H
#include <sys.h>	
#define ENCODER_TIM_PERIOD (u16)(65535)   //不可大于65535 因为F103的定时器是16位的
void Encoder_Init_TIM2(void);
void Encoder_Init_TIM4(void);
int Read_Encoder(u8 TIMX);
#endif

第三步 编写main.c文件

(3) (6)将工程编译需要用到的头文件包含进来,并预定义显示函数和全局变量。

#include "encoder.h" //包含编码器函数头文件
#include "sys.h"   //包含系统头文件
#include "stm32f10x.h" //包含系统寄存器定义声明的头文件

void oled_show(void);
int Encoder_Left,Encoder_Right;   //左右编码器的脉冲计数

(4) (7)在主函数中调用延时函数、显示函数和编码器函数的初始化函数。

int main(void)
{ 
delay_init();	   //延时函数初始化
	OLED_Init();     //OLED初始化
		Encoder_Init_TIM2();            //编码器接口初始化
		Encoder_Init_TIM4();            //编码器接口初始化

(5) (8)定义主循环,在主循环中调用超声波读取函数和显示函数。

	while(1)
	{
		Encoder_Left=Read_Encoder(2);       //读取左侧编码器值
		Encoder_Right=Read_Encoder(4);      //读取右侧编码器值
		oled_show();          //显示屏打开
		delay_ms(50); 
	}

(6) (9)编写OLED显示函数

void oled_show(void)
{
//显示右侧编码器返回值
	OLED_ShowString(0,10,"Encoder_Right");
	if(Encoder_Right >=0)OLED_ShowString(20,20,""),
		OLED_ShowNumber(45,20,Encoder_Right,4,12);	
	else OLED_ShowString(20,20,"-"),
		OLED_ShowNumber(45,20,4-Encoder_Right,4,12);
//显示左侧编码器返回值
	OLED_ShowString(0,40,"Encoder_Left");
	if(Encoder_Left>=0)OLED_ShowString(10,50,""),
		OLED_ShowNumber(45,50,Encoder_Left,4,12);
	else OLED_ShowString(10,50,"-"),
		OLED_ShowNumber(45,50,-Encoder_Left,4,12);
	//=============刷新======================//
	OLED_Refresh_Gram();	
}

第四步 编译并下载,观察实验现象

(10)本实验采用仿真器为STLink V2,将仿真器与小车相连,注意正负极不要接反,如图7所示。
在这里插入图片描述
图7仿真器与下载线连接图

(11)编译程序:点击如图8所示的编译按键。
在这里插入图片描述
图8Keil编译环境下的编译按键

(12)当编译完成后,如果没有问题,Build Output栏会出现无错误、无警告的提示,如图9所示。
在这里插入图片描述
图9编译通过后Build Output栏提示信息

(13)下载程序:点击如图所示的下载按键,程序就会下载到STM32的芯片中。下载按键如图10所示。
在这里插入图片描述
图10Keil编译环境下的下载按键

(14)观察实验现象,OLED显示屏上显示出当前电机的转速,用手从不同方向转动电机,观察数值的变化,结果如图11所示。
在这里插入图片描述
图11平衡车上的编码器测量数据

【思考题】

1、选择题

题目1:编码器传回的正交编码中,两个信号的相位差是多少(B)
A:60度
B:90度
C:120度
D:180度

题目2:若要配置一个8位的编码器,在encoder.h中的自动重载值应该设为多少(B)
A:8
B:255
C:256
D:65535

2、简答题

题目1:简要说明如何将编码器的输出转换为方向信息和速率信息。
编码器输出为AB两相脉冲信号,A相和B相之间的相位差为90度。单片机在接收到AB相输入后,可以根据收到相位的先后顺序确定电机的转向,根据脉冲序列的频率确定电机的转速。

题目2:结合相关资料,试说明如何通过IO和中断对编码器的输出数据进行解码。

(开放性题目,答案不唯一)配置两个IO口为上升下降沿触发中断,设置全局变量表示转速、方向,设置表示跳变的标志位,指定其中一个输入,若该输入触发中断,则转速变量加1,检测相应的寄存器,若为上升沿触发则跳变标志位置1,若为下降沿触发则跳变标志位置0;当另一输入进入中断时,可以根据自身是上升沿还是下降沿触发结合方向标志位,确定自身是超前于另一信号,还是滞后于另一信号,从而确定方向。


2013-01-11 12:22:15 liwliwliw 阅读数 2321
  • 温度传感器DS18B20-第2季第1部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第1个课程,主要讲解单片机系统中常用的温度传感器DS18B20。本课程的目标是让大家进一步掌握时序的分析和编程实现,学会移植和调试DS18B20的程序,能够读取温度。

    2184 人正在学习 去看看 朱有鹏

/*
 * ADXL345模块
 * 
 * 用途:ADXL345模块IIC测试程序
 * 
 * 作者 日期 备注
 * leevy 2013 1 8    新增
 
 * 
 */


#include  <REG51.H>
#include  <math.h>    //Keil library  
#include  <stdio.h>   //Keil library
#include  <INTRINS.H>
#define   uchar unsigned char
#define   uint unsigned int
#define   DataPort P0    //LCD1602数据端口
sbit  SCL=P1^0;      //IIC时钟引脚定义
sbit  SDA=P1^1;      //IIC数据引脚定义
sbit      LCM_RS=P2^6;   //LCD1602命令端口
sbit      LCM_RW=P2^5;   //LCD1602命令端口
sbit      LCM_EN=P2^7;   //LCD1602命令端口


#define SlaveAddress   0xA6 //定义器件在IIC总线中的从地址,根据ALT  ADDRESS地址引脚不同修改
                              //ALT  ADDRESS引脚接地时地址为0xA6,接电源时地址为0x3A
typedef unsigned char  BYTE;
typedef unsigned short WORD;


BYTE BUF[8];                         //接收数据缓存区      
uchar ge,shi,bai,qian,wan;           //显示变量
int  dis_data;                       //变量


void delay(unsigned int k);
void InitLcd();                      //初始化lcd1602
void Init_ADXL345(void);             //初始化ADXL345


void WriteDataLCM(uchar dataW);
void WriteCommandLCM(uchar CMD,uchar Attribc);
void DisplayOneChar(uchar X,uchar Y,uchar DData);
void conversion(uint temp_data);


void  Single_Write_ADXL345(uchar REG_Address,uchar REG_data);   //单个写入数据
uchar Single_Read_ADXL345(uchar REG_Address);                   //单个读取内部寄存器数据
void  Multiple_Read_ADXL345();                                  //连续的读取内部寄存器数据
//------------------------------------
void Delay5us();
void Delay5ms();
void ADXL345_Start();
void ADXL345_Stop();
void ADXL345_SendACK(bit ack);
bit  ADXL345_RecvACK();
void ADXL345_SendByte(BYTE dat);
BYTE ADXL345_RecvByte();
void ADXL345_ReadPage();
void ADXL345_WritePage();
//-----------------------------------


//*********************************************************
void conversion(uint temp_data)  
{  
    wan=temp_data/10000+0x30 ;
    temp_data=temp_data%10000;   //取余运算
qian=temp_data/1000+0x30 ;
    temp_data=temp_data%1000;    //取余运算
    bai=temp_data/100+0x30   ;
    temp_data=temp_data%100;     //取余运算
    shi=temp_data/10+0x30    ;
    temp_data=temp_data%10;      //取余运算
    ge=temp_data+0x30;
}


/*******************************/
void delay(unsigned int k)
{
unsigned int i,j;
for(i=0;i<k;i++)
{
for(j=0;j<121;j++)
{;}}
}
/*******************************/
void WaitForEnable(void)
{
DataPort=0xff;
LCM_RS=0;LCM_RW=1;_nop_();
LCM_EN=1;_nop_();_nop_();
while(DataPort&0x80);
LCM_EN=0;
}
/*******************************/
void WriteCommandLCM(uchar CMD,uchar Attribc)
{
if(Attribc)WaitForEnable();
LCM_RS=0;LCM_RW=0;_nop_();
DataPort=CMD;_nop_();
LCM_EN=1;_nop_();_nop_();LCM_EN=0;
}
/*******************************/
void WriteDataLCM(uchar dataW)
{
WaitForEnable();
LCM_RS=1;LCM_RW=0;_nop_();
DataPort=dataW;_nop_();
LCM_EN=1;_nop_();_nop_();LCM_EN=0;
}
/***********************************/
void InitLcd()
{
WriteCommandLCM(0x38,1);
WriteCommandLCM(0x08,1);
WriteCommandLCM(0x01,1);
WriteCommandLCM(0x06,1);
WriteCommandLCM(0x0c,1);
}
/***********************************/
void DisplayOneChar(uchar X,uchar Y,uchar DData)
{
Y&=1;
X&=15;
if(Y)X|=0x40;
X|=0x80;
WriteCommandLCM(X,0);
WriteDataLCM(DData);
}


/**************************************
延时5微秒(STC90C52RC@12M)
不同的工作环境,需要调整此函数,注意时钟过快时需要修改
当改用1T的MCU时,请调整此延时函数
**************************************/
void Delay5us()
{
    _nop_();_nop_();_nop_();_nop_();
    _nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
}


/**************************************
延时5毫秒(STC90C52RC@12M)
不同的工作环境,需要调整此函数
当改用1T的MCU时,请调整此延时函数
**************************************/
void Delay5ms()
{
    WORD n = 560;


    while (n--);
}


/**************************************
起始信号
**************************************/
void ADXL345_Start()
{
    SDA = 1;                    //拉高数据线
    SCL = 1;                    //拉高时钟线
    Delay5us();                 //延时
    SDA = 0;                    //产生下降沿
    Delay5us();                 //延时
    SCL = 0;                    //拉低时钟线
}


/**************************************
停止信号
**************************************/
void ADXL345_Stop()
{
    SDA = 0;                    //拉低数据线
    SCL = 1;                    //拉高时钟线
    Delay5us();                 //延时
    SDA = 1;                    //产生上升沿
    Delay5us();                 //延时
}


/**************************************
发送应答信号
入口参数:ack (0:ACK 1:NAK)
**************************************/
void ADXL345_SendACK(bit ack)
{
    SDA = ack;                  //写应答信号
    SCL = 1;                    //拉高时钟线
    Delay5us();                 //延时
    SCL = 0;                    //拉低时钟线
    Delay5us();                 //延时
}


/**************************************
接收应答信号
**************************************/
bit ADXL345_RecvACK()
{
    SCL = 1;                    //拉高时钟线
    Delay5us();                 //延时
    CY = SDA;                   //读应答信号
    SCL = 0;                    //拉低时钟线
    Delay5us();                 //延时


    return CY;
}


/**************************************
向IIC总线发送一个字节数据
**************************************/
void ADXL345_SendByte(BYTE dat)
{
    BYTE i;


    for (i=0; i<8; i++)         //8位计数器
    {
        dat <<= 1;              //移出数据的最高位
        SDA = CY;               //送数据口
        SCL = 1;                //拉高时钟线
        Delay5us();             //延时
        SCL = 0;                //拉低时钟线
        Delay5us();             //延时
    }
    ADXL345_RecvACK();
}


/**************************************
从IIC总线接收一个字节数据
**************************************/
BYTE ADXL345_RecvByte()
{
    BYTE i;
    BYTE dat = 0;


    SDA = 1;                    //使能内部上拉,准备读取数据,
    for (i=0; i<8; i++)         //8位计数器
    {
        dat <<= 1;
        SCL = 1;                //拉高时钟线
        Delay5us();             //延时
        dat |= SDA;             //读数据               
        SCL = 0;                //拉低时钟线
        Delay5us();             //延时
    }
    return dat;
}


//******单字节写入*******************************************


void Single_Write_ADXL345(uchar REG_Address,uchar REG_data)
{
    ADXL345_Start();                  //起始信号
    ADXL345_SendByte(SlaveAddress);   //发送设备地址+写信号
    ADXL345_SendByte(REG_Address);    //内部寄存器地址,请参考中文pdf22页 
    ADXL345_SendByte(REG_data);       //内部寄存器数据,请参考中文pdf22页 
    ADXL345_Stop();                   //发送停止信号
}


//********单字节读取*****************************************
uchar Single_Read_ADXL345(uchar REG_Address)
{  uchar REG_data;
    ADXL345_Start();                          //起始信号
    ADXL345_SendByte(SlaveAddress);           //发送设备地址+写信号
    ADXL345_SendByte(REG_Address);            //发送存储单元地址,从0开始
    ADXL345_Start();                          //起始信号
    ADXL345_SendByte(SlaveAddress+1);         //发送设备地址+读信号
    REG_data=ADXL345_RecvByte();              //读出寄存器数据
ADXL345_SendACK(1);   
ADXL345_Stop();                           //停止信号
    return REG_data; 
}
//*********************************************************
//
//连续读出ADXL345内部加速度数据,地址范围0x32~0x37
//
//*********************************************************
void Multiple_read_ADXL345(void)
{   uchar i;
    ADXL345_Start();                          //起始信号
    ADXL345_SendByte(SlaveAddress);           //发送设备地址+写信号
    ADXL345_SendByte(0x32);                   //发送存储单元地址,从0x32开始
    ADXL345_Start();                          //起始信号
    ADXL345_SendByte(SlaveAddress+1);         //发送设备地址+读信号
for (i=0; i<6; i++)                      //连续读取6个地址数据,存储中BUF
    {
        BUF[i] = ADXL345_RecvByte();          //BUF[0]存储0x32地址中的数据
        if (i == 5)
        {
           ADXL345_SendACK(1);                //最后一个数据需要回NOACK
        }
        else
        {
          ADXL345_SendACK(0);                //回应ACK
       }
   }
    ADXL345_Stop();                          //停止信号
    Delay5ms();
}




//*****************************************************************


//初始化ADXL345,根据需要请参考pdf进行修改************************
void Init_ADXL345()
{
   Single_Write_ADXL345(0x31,0x0B);   //测量范围,正负16g,13位模式
   Single_Write_ADXL345(0x2C,0x08);   //速率设定为12.5 参考pdf13页
   Single_Write_ADXL345(0x2D,0x08);   //选择电源模式   参考pdf24页
   Single_Write_ADXL345(0x2E,0x80);   //使能 DATA_READY 中断
   Single_Write_ADXL345(0x1E,0x00);   //X 偏移量 根据测试传感器的状态写入pdf29页
   Single_Write_ADXL345(0x1F,0x00);   //Y 偏移量 根据测试传感器的状态写入pdf29页
   Single_Write_ADXL345(0x20,0x05);   //Z 偏移量 根据测试传感器的状态写入pdf29页
}


//
//
//
//
//
//
//void send_data(uchar da)
//{
// SBUF=da;
// while(!TI);
// TI=0;
//}
////显示x轴
//void send_x()
//{   
// float temp;
//
// send_data('x');//x  
//    dis_data=(BUF[1]<<8)+BUF[0];  //合成数据 
// if(dis_data<0)
// {
// dis_data=-dis_data;
//    send_data('-');    //显示正负符号位
// }
// else send_data('+'); //
//
//    temp=(float)dis_data*3.9;  //计算数据和显示,查考ADXL345快速入门第4页
//    conversion(temp);          //转换出显示需要的数据
//// send_data(qian*16+bai);
//// send_data(shi*16+ge);
// send_data(qian);
// send_data(bai);
// send_data(shi);
// send_data(ge);
//}
//
////***********************************************************************
////显示y轴
//void send_y()
//{   
//
// float temp;
//
// send_data('y');  
//    dis_data=(BUF[3]<<8)+BUF[2];  //合成数据   
// if(dis_data<0)
// {
// dis_data=-dis_data;
//    send_data('-');    //显示正负符号位
// }
// else send_data('+'); //显示空格
//
//    temp=(float)dis_data*3.9;  //计算数据和显示,查考ADXL345快速入门第4页
//    conversion(temp);          //转换出显示需要的数据
//// send_data(qian*16+bai);
//// send_data(shi*16+ge);
// send_data(qian);
// send_data(bai);
// send_data(shi);
// send_data(ge);
//
//}
//
////***********************************************************************
////显示z轴
//void send_z()
//{      
//
// float temp;
//
// send_data('z'); 
//    dis_data=(BUF[5]<<8)+BUF[4];    //合成数据   
// if(dis_data<0)
// {
// dis_data=-dis_data;
//    send_data('-');    //显示正负符号位
// }
// else send_data('+'); //显示空格
//
//    temp=(float)dis_data*3.9;  //计算数据和显示,查考ADXL345快速入门第4页
//    conversion(temp);          //转换出显示需要的数据
//// send_data(qian*16+bai);
//// send_data(shi*16+ge);
// send_data(qian);
// send_data(bai);
// send_data(shi);
// send_data(ge);
//
//
//}
//
//
















 
//***********************************************************************
//显示x轴
void display_x()
{   
float temp;
// send_data('x');//x  
    dis_data=(BUF[1]<<8)+BUF[0];  //合成数据   
if(dis_data<0)
{
dis_data=-dis_data;
   DisplayOneChar(10,0,'-');      //显示正负符号位
// send_data('-');
}
else 
{
DisplayOneChar(10,0,'+'); //显示空格
// send_data('+');

}



    temp=(float)dis_data*3.9;  //计算数据和显示,查考ADXL345快速入门第4页
    conversion(temp);          //转换出显示需要的数据
DisplayOneChar(8,0,'X');
    DisplayOneChar(9,0,':'); 
    DisplayOneChar(11,0,qian); 
DisplayOneChar(12,0,'.'); 
    DisplayOneChar(13,0,bai); 
    DisplayOneChar(14,0,shi); 
DisplayOneChar(15,0,ge); 
// send_data(qian);
// send_data(bai);
// send_data(shi);
// send_data(ge);
}


//***********************************************************************
//显示y轴
void display_y()
{     
float temp;
// send_data('y');
    dis_data=(BUF[3]<<8)+BUF[2];  //合成数据   
if(dis_data<0)
{
dis_data=-dis_data;
   DisplayOneChar(2,1,'-');      //显示正负符号位
// send_data('-');
}
else 
{
DisplayOneChar(2,1,'+'); //显示空格
// send_data('+');
}


    temp=(float)dis_data*3.9;  //计算数据和显示,查考ADXL345快速入门第4页
    conversion(temp);          //转换出显示需要的数据
DisplayOneChar(0,1,'Y');   //第1行,第0列 显示y
    DisplayOneChar(1,1,':'); 
    DisplayOneChar(3,1,qian); 
DisplayOneChar(4,1,'.'); 
    DisplayOneChar(5,1,bai); 
    DisplayOneChar(6,1,shi);  
DisplayOneChar(7,1,ge); 
// send_data(qian);
// send_data(bai);
// send_data(shi);
// send_data(ge); 
}


//***********************************************************************
//显示z轴
void display_z()
{
    float temp;
// send_data('z');
    dis_data=(BUF[5]<<8)+BUF[4];    //合成数据   
if(dis_data<0)
{
dis_data=-dis_data;
   DisplayOneChar(10,1,'-');       //显示负符号位
// send_data('-');
}
else 
{
DisplayOneChar(10,1,' ');  //显示空格
// send_data('+');
}


    temp=(float)dis_data*3.9;  //计算数据和显示,查考ADXL345快速入门第4页
    conversion(temp);          //转换出显示需要的数据
DisplayOneChar(8,1,'Z');  //第0行,第10列 显示Z
    DisplayOneChar(9,1,':'); 
    DisplayOneChar(11,1,qian); 
DisplayOneChar(12,1,'.'); 
    DisplayOneChar(13,1,bai); 
    DisplayOneChar(14,1,shi); 
DisplayOneChar(15,1,ge);
// send_data(qian);
// send_data(bai);
// send_data(shi);
// send_data(ge);  
}
/*
void ser_int()
{
        SCON = 0x50;       //REN=1允许串行接受状态,串口工作模式2       
   TMOD|= 0x20;      //定时器工作方式2                       
PCON|= 0x80;      //波特率提高一倍                                                    
      // TH1  = 0xFD;     //baud*2  /* reload value 19200、数据位8、停止位1。效验位无(11.0592)     
   TH1 = 0xF3;// //baud*2  /*  波特率4800、数据位8、停止位1。效验位无 (12M)
    TL1 = 0xF3; 
TR1  = 1;        //开启定时器1                                                      
ES   = 1;        //开串口中断                  
EA   = 1;        // 开总中断 
  // IE = 0x0;

*/


//*********************************************************
//******主程序********
//*********************************************************
void main()

uchar devid;
delay(500);                   //上电延时
InitLcd();                      //液晶初始化ADXL345
// ser_int();
DisplayOneChar(0,0,'A');
DisplayOneChar(1,0,'D'); 
DisplayOneChar(2,0,'X'); 
DisplayOneChar(3,0,'L'); 
DisplayOneChar(4,0,'3'); 
DisplayOneChar(5,0,'4');  
DisplayOneChar(6,0,'5'); 

Init_ADXL345();                 //初始化ADXL345
devid=Single_Read_ADXL345(0X00);//读出的数据为0XE5,表示正确
while(1)                         //循环

Multiple_Read_ADXL345();       //连续读出数据,存储在BUF中
display_x();                   //---------显示X轴
display_y();                   //---------显示Y轴
display_z();                   //---------显示Z轴
delay(200);                    //延时    
// send_x();
// send_y();
// send_z();        
}

2019-03-29 15:18:22 woshi_ziyu 阅读数 89
  • 温度传感器DS18B20-第2季第1部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第1个课程,主要讲解单片机系统中常用的温度传感器DS18B20。本课程的目标是让大家进一步掌握时序的分析和编程实现,学会移植和调试DS18B20的程序,能够读取温度。

    2184 人正在学习 去看看 朱有鹏

霍尔传感器的工作原理是基于1869年Edwin Hall提出的霍尔效应。提出的声明说:“霍尔效应是在电导体上产生电压差(霍尔电压),横向于导体中的电流和垂直于电流的外加磁场。“

那么,什么可以是最简单的声明形式,以更好的方式理解它?在本本中,将通过实际示例逐步说明。霍尔传感器将与Atmega16微控制器连接,一个LED将用于显示磁铁将被带到霍尔传感器附近时的效果。

在这里插入图片描述

什么是霍尔效应?

霍尔效应与磁场中的移动电荷有关。要以实用的方式理解,请将电池连接到导体,如下图(a)所示。电流(i)将开始从导体流过电池的正极到负极。

在这里插入图片描述

电子流(e-)将与电流的方向相反,即从电池的负极端子通过导体到电池的正极端子。在我们测量导体之间的电压的时刻,如下面的图像(b)所示,那么电压将为零,即电位差将为零。

更多内容请参考以下链接:https://www.yiboard.com/thread-1127-1-1.html

2012-09-20 14:53:45 I_am_growing 阅读数 2215
  • 温度传感器DS18B20-第2季第1部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第1个课程,主要讲解单片机系统中常用的温度传感器DS18B20。本课程的目标是让大家进一步掌握时序的分析和编程实现,学会移植和调试DS18B20的程序,能够读取温度。

    2184 人正在学习 去看看 朱有鹏
/****************************************************************
                外部晶振8M
				PA0~3:四位数码管的位选
				PB0~7:数码管的8位段选
				外部中断0用于计数
				定时器0溢出中断的定时为1ms
				说明 :检测到水流较小时,继电器延时1秒关闭
******************************************************************/				
#include<iom16v.h>
#include<macros.h>
#define uchar unsigned char
#define uint unsigned int

 char led_7[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};  //数码管段选
 
 char position[4]={0xfe,0xfd,0xfb,0xf7};//数码管位选
 
 uint sumnum=0;  //用于记录1000ms内进入中断的次数
 
 uint time=0;   //记录进入比较定时器0的次数
 
 uint num=0;     //记录1ms内进入中断的次数
 
 uint count=0;   //进入外部中断0的次数
 
 uchar flag;
 
 uint sumnum1;   //记录100ms内的数目
 
/***************************函数声明***************************/
 
 void delay();
 
 void display(uint m );
 
 void init();
 
 void init_0();
 
 void init_2();
 void _delay_us(uint l)
{
 	 unsigned int i;
	 for(i=0;i<l;i++)
	 {
	 asm("nop");
	 }
}
 
 
/**************************主函数***********************************/

 void main()
{
 	 init();
	 init_0();
 	 init_2();
	 while(sumnum<5)
	 {
	  	PORTD=0XBF;
	    segdisplay(sumnum1);
	 }
	while(1)
	{
	    segdisplay(sumnum1);
	}
	 
}
 
 /*************************扫描数码管时的延时函数*********************/
 
 void delay()
 {
  	  uchar i,j;
	  for(i=6;i>0;i--)
	  for(j=225;j>0;j--);
 }
 
 /************************数码管显示函数*****************************/
 
void segdisplay( int temp)
	 {
	  int seg[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
	  int temp1,temp2,temp3,temp4;
	  temp1=temp/1000;
	  temp2=(temp/100)%10;
	  temp3=(temp/10)%10;
	  temp4=temp%10;
	  DDRB=0xff;
	  DDRA|=0x0f;
	    PORTA=~BIT(3);
	  	PORTB=seg[temp1];
		_delay_us(100);
		PORTA=~BIT(2);
		PORTB=seg[temp2];
		_delay_us(100);
		PORTA=~BIT(1);
		PORTB=seg[temp3];
		_delay_us(100);
		PORTA=~BIT(0);
		PORTB=seg[temp4];
		_delay_us(100);
	}
 
 /***********************管脚初始化函数*********************/
 
 void init()
 {
  	  
	  	  DDRD|=0X40;   //PD4 设置为输出
		  PORTD=0XBF;   
	  	  DDRA=0XFF;
		  DDRB=0XFF;
		  PORTA=0XFF;
		  PORTB=0XFF;
 }
 
 /***********************外部中断0初始化*********************/
 
 void init_0()
 {
  	  MCUCR=0X02;   //INT0为下降沿触发
	  GICR=0X40;   //使能INT0中断
	  SREG=0X80;  //使能总中断
 }
 
 /**********************定时器2初始化***********************/
 
 void init_2()
 {
  	  TCCR0=0x03;   // 内部时钟,64 分频(8M/64=125KHz) 
      TCNT0=0x83;   //装初值  
      TIMSK=0x01;   // 允许 T/C0溢出中断中断  
 }


 /***********************外部中断0子函数********************/
	 #pragma interrupt_handler int0_isr:2
	 void int0_isr(void)
	 {
	  	  count++;
	 } 
	/*********************定时计数器0溢出中断子函数*****************/
	#pragma interrupt_handler int0_over:10 
	void int0_over(void)
	{
	 	TCNT0=0x83;   //重装初值 
		if((time%100) == 0)
			sumnum1 = num;
	 	if(time == 1000)
		{
			sumnum=num;
			if(sumnum<10)
			{
			    
				if((flag==1)&&(sumnum<10))
				{
				 	PORTD=0XFF;
					flag=0;
				}				
					flag++;
			}
			else
					PORTD=0XBF;
			num=0;
			time=0;
		}
	 	num+=count;
		count=0;
		++time; 
	} 

没有更多推荐了,返回首页