嵌入式学习记录- 13-1~15-3

13-1 DS18B20温度传感器

2023 04 27

通信接口:1-Wire(单总线)

其它特征:可形成总线结构、内置温度报警功能、可寄生供电

引脚功能
VDD电源(3.0V ~ 5.5V)
GND电源地
DQ单总线接口

64-BIT ROM:作为器件地址,用于总线通信的寻址

SCRATCHPAD(暂存器):用于总线的数据交互

EEPROM:用于保存温度触发阈值和配置参数

单总线 1-Wire BUS

一根通信线:DQ

异步、半双工

采用寄生供电时还可以免去一根VSS线路,只需要DQ+GND两根线

线路规范:

  • 设备的DQ均要配置成开漏输出模式

  • DQ添加一个上拉电阻,阻值一般为4.7KΩ左右

  • 若此总线的从机采取寄生供电,则主机还应配一个强上拉输出电路

单总线时序结构

  • 初始化:主机将总线拉低至少480us,然后释放总线,等待15~60us后,存在的从机会拉低总线60~240us以响应主机,之后从机将释放总线

  • 发送一位:主机将总线拉低60~120us,然后释放总线,表示发送0;主机将总线拉低1~15us,然后释放总线,表示发送1。从机将在总线拉低30us后(典型值)读取电平,整个时间片应大于60us

  • 接收一位:主机将总线拉低1~15us,然后释放总线,并在拉低后15us内读取总线电平(尽量贴近15us的末尾),读取为低电平则为接收0,读取为高电平则为接收1 ,整个时间片应大于60us

  • 发送一个字节:连续调用8次发送一位的时序,依次发送一个字节的8位(低位在前)

  • 接收一个字节:连续调用8次接收一位的时序,依次接收一个字节的8位(低位在前)

DS1802操作流程

  • 初始化:从机复位,主机判断从机是否响应

  • ROM操作:ROM指令+本指令需要的读写操作

  • 功能操作(RAM操作):功能指令+本指令需要的读写操作

ROM****指令功能指令
SEARCH ROM [F0h] 搜索ROMCONVERT T [44h] 温度更新
READ ROM [33h] 读ROMWRITE SCRATCHPAD [4Eh] 写暂存器
MATCH ROM [55h] 匹配ROMREAD SCRATCHPAD [BEh] 读暂存器
SKIP ROM [CCh] 跳过ROM(只有一个从机的情况下使用)COPY SCRATCHPAD [48h] 配置持久化
ALARM SEARCH [ECh] 报警搜索RECALL E2 [B8h] 使用配置
READ POWER SUPPLY [B4h] 读是否为寄生供电

DS18B20数据帧:

  • 温度变换:初始化→跳过ROM →开始温度变换

  • 温度读取:初始化→跳过ROM →读暂存器→连续的读操作

温度存储格式:

其实就是补码啦

13-2 DS18B20温度读取

2023 04 27

温度转换需要时间

main.c

#include <REGX52.H>
#include "LCD1602.h"
#include "DS18B20.h"
#include "Delay.h"

float T;
unsigned int i;

int main(){
    LCD_Init();
    LCD_ShowString(1, 1, "temperature:");
    while(1){
        Delay(1000);
        DS18B20_ConvertT();
        T = DS18B20_ReadT();
        if (T < 0) {// 显示符号
            LCD_ShowChar(2, 1, '-');
        }
        else{
            LCD_ShowChar(2, 1, '+');
        }
        LCD_ShowNum(2, 2, T, 3);// 显示整数部分
        LCD_ShowChar(2, 5, '.');
        LCD_ShowNum(2, 6, ((unsigned long) (T * 10000)) % 10000, 4);// 显示小数部分
    }
}

DS18B20.c

#include <REGX52.H>
#include "OneWire.h"

#define DS18B20_SKIP_ROM        0xCC
#define DS18B20_CONVERT_T       0x44
#define DS18B20_READ_SCRATCHPAD 0xBE

/**
 * @brief DS18B20温度转换  温度转换需要时间
 */
void DS18B20_ConvertT(){
    OneWire_Init();
    OneWire_SendByte(DS18B20_SKIP_ROM);
    OneWire_SendByte(DS18B20_CONVERT_T);
}

/**
 * @brief 读取温度
 * @return 读取的温度值,默认精度为2^-4
 */
float DS18B20_ReadT(){// 51单片机对浮点数的处理能力较弱,尽量不要用float
    unsigned char TLSB,TMSB;
    int Temp;
    float T;
    OneWire_Init();
    OneWire_SendByte(DS18B20_SKIP_ROM);
    OneWire_SendByte(DS18B20_READ_SCRATCHPAD);
    TLSB = OneWire_ReceiveByte();
    TMSB = OneWire_ReceiveByte();
    Temp = (TMSB << 8) | TLSB;
    T=Temp / 16.0;

    return T;
}

DS18B20.h

#ifndef __DS19B20_H__
#define __DS19B20_H__

void DS18B20_ConvertT();
float DS18B20_ReadT();

#endif

OneWire.c

#include <REGX52.H>

sbit OneWire_DQ=P3^7;

/**
 * @brief 1Wire总线初始化
 * @return AckBit,0~1,0表示从机存在,1表示不存在
 */
unsigned char OneWire_Init(){
    unsigned char i,AckBit;

    OneWire_DQ = 1;
    OneWire_DQ = 0;
    i = 227;while (--i);// 拉低500us @11.0592MHz
    OneWire_DQ = 1;
    i = 29;while (--i);// 放权70us
    AckBit = OneWire_DQ;// 读取响应值
    i = 227;while (--i);// 等待500us @11.0592MHz
    return AckBit;
}

/**
 * @brief 1Wire总线发送一位
 * @param Bit 发送位
 */
void OneWire_SendBit(unsigned char Bit){
    unsigned char i;

    OneWire_DQ = 0;
    i = 4;while (--i);// Delay 10us
    OneWire_DQ = Bit;
    i = 29;while (--i);// Delay 70us
    OneWire_DQ = 1;
}

/**
 * @brief 1Wire总线读取一位
 * @return 0~1
 */
unsigned char OneWire_ReceiveBit(){
    unsigned char i,Bit;

    OneWire_DQ = 0;
    i = 2;while (--i);// Delay 5us
    OneWire_DQ = 1;
    i = 4;while (--i);// Delay 10us
    Bit = OneWire_DQ;
    i = 20;while (--i);// Delay 50us
    return Bit;
}

/**
 * @brief 1Wire总线发送一个字节
 * @param Byte 要发送的字节
 */
void OneWire_SendByte(unsigned char Byte){
    unsigned char i;
    for(i=0;i<8;i++){
        OneWire_SendBit(Byte & (0x01 << i));// 从低位到高位
    }
}

/**
 * @brief 1Wire总线读取一个字节
 * @return 读取到的字节
 */
unsigned char OneWire_ReceiveByte(){
    unsigned char i,Byte = 0;// Byte没有直接赋值,记得赋初值0
    for(i=0;i<8;i++){
        if(OneWire_ReceiveBit()) Byte |= (0x01 << i);
    }

    return Byte;
}

OneWire.h

#ifndef __ONE_WIRE_H__
#define __ONE_WIRE_H__

unsigned char OneWire_Init(void);
void OneWire_SendBit(unsigned char Bit);
unsigned char OneWire_ReceiveBit();
void OneWire_SendByte(unsigned char Byte);
unsigned char OneWire_ReceiveByte();

#endif

13-3 DS18B20温度报警器

2023 04 28

main.c

#include <REGX52.H>
#include "LCD1602.h"
#include "DS18B20.h"
#include "Delay.h"
#include "Key.h"
#include "AT24C02.h"
#include "Timer0.h"

float T;
unsigned int i;
char TLow, THigh;
unsigned char KeyNum;

int main() {
    LCD_Init();
    Timer0Init();

    LCD_ShowString(1, 1, "T:");
    LCD_ShowString(2, 1, "TH:");
    LCD_ShowString(2, 9, "TL:");
    THigh = AT24C02_ReadByte(0);
    TLow = AT24C02_ReadByte(1);
    if (THigh > 125) THigh = 125;
    else if (THigh < -55) THigh = -55;
    if (TLow > 125) TLow = 125;
    else if (TLow < -55) TLow = -55;
    if (THigh < TLow) THigh = TLow;
    DS18B20_ConvertT();
    Delay(1000);

    while (1) {
        KeyNum = Key();
        DS18B20_ConvertT();
        T = DS18B20_ReadT();
        if (T < 0) {// 显示符号
            LCD_ShowChar(1, 3, '-');
        } else {
            LCD_ShowChar(1, 3, '+');
        }
        LCD_ShowNum(1, 4, (T < 0 ? -T : T), 3);// 显示整数部分
        LCD_ShowChar(1, 7, '.');
        LCD_ShowNum(1, 8, ((unsigned long) ((T < 0 ? -T : T) * 10000)) % 10000, 4);// 显示小数部分

        // 阈值判断及显示
        if (KeyNum) {
            if (KeyNum == 1) {
                if (THigh < 125) THigh++;
                AT24C02_WriteByte(0,THigh);
            } else if (KeyNum == 2) {
                if (THigh > TLow) THigh--;
                AT24C02_WriteByte(0,THigh);
            } else if (KeyNum == 3) {
                if (TLow < THigh) TLow++;
                AT24C02_WriteByte(1,TLow);
            } else if (KeyNum == 4) {
                if (TLow > -55) TLow--;
                AT24C02_WriteByte(1,TLow);
            }
            KeyNum = 0;
        }

        LCD_ShowSignedNum(2, 4, THigh, 3);
        LCD_ShowSignedNum(2, 12, TLow, 3);

        if (T > THigh) {
            LCD_ShowString(1, 13, "OV:H");
        } else if (T < TLow) {
            LCD_ShowString(1, 13, "OV:L");
        } else {
            LCD_ShowString(1, 13, "    ");
        }
    }
}

// 中断函数
void timer0_Routine() interrupt 1
{
    static unsigned int T0Count;
    static unsigned char K;
    TL0 = 0x66;        //设置定时初值
    TH0 = 0xFC;        //设置定时初值
    T0Count++;
    if (T0Count >= 20) {
        K = Key();
        if (K) KeyNum = K;
        T0Count = 0;
    }
}

Key.c

#include <REGX52.H>
#include "Delay.h"

/**
 * @brief 获取独立按键键码
 * @return 按下按键的键码,范围:0~4
 */
unsigned char Key() {
    static unsigned char KeyNumber = 0;
    static unsigned char K;

    if (KeyNumber) {


        if (KeyNumber == 1 && P3_1 == 1) {K = KeyNumber; KeyNumber = 0; return K;}
        else if (KeyNumber == 2 && P3_0 == 1) {K = KeyNumber; KeyNumber = 0; return K;}
        else if (KeyNumber == 3 && P3_2 == 1) {K = KeyNumber; KeyNumber = 0; return K;}
        else if (KeyNumber == 4 && P3_3 == 1) {K = KeyNumber; KeyNumber = 0; return K;}

        return 0;
    } else {
        if (P3_1 == 0) { KeyNumber = 1; }
        if (P3_0 == 0) { KeyNumber = 2; }
        if (P3_2 == 0) { KeyNumber = 3; }
        if (P3_3 == 0) { KeyNumber = 4; }

        return 0;
    }
}

在OneWire中加入了EA=0;EA=1;因为在1Wire(单总线)通信时对时序有着严格的要求,中断会导致时序出错。

14-1 LCD1602

2023 04 28

引脚功能
VSS
VDD电源正极(4.5~5.5V)
VO对比度调节电压
RS数据/指令选择,1为数据,0为指令
RW读/写选择,1为读,0为写
E使能,1为数据有效,下降沿执行命令
D0~D7数据输入/输出
A背光灯电源正极
K背光灯电源负极

时序结构

  • 写数据/指令

  • 初始化:

    • 发送指令0x38 //八位数据接口,两行显示,5*7点阵

    • 发送指令0x0C //显示开,光标关,闪烁关

    • 发送指令0x06 //数据读写操作后,光标自动加一,画面不动

    • 发送指令0x01 //清屏

  • 显示字符:

    • 发送指令0x80|AC //设置光标位置
    • 发送数据 //发送要显示的字符数据
    • 发送数据 //发送要显示的字符数据
    • ……

14-2 LCD1602液晶显示屏

2023 04 28

LCD1602.c

#include <REGX52.H>

//引脚配置:
sbit LCD_RS = P2^6;
sbit LCD_RW = P2^5;
sbit LCD_EN = P2^7;
#define LCD_DataPort P0

//函数定义:
/**
  * @brief  LCD1602延时函数,11.0592MHz调用可延时1ms
  * @param  无
  * @retval 无
  */
void LCD_Delay(){
    unsigned char i, j;

//    _nop_();
    i = 2;
    j = 199;
    do
    {
        while (--j);
    } while (--i);
}

/**
  * @brief  LCD1602写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void LCD_WriteCommand(unsigned char Command){
    LCD_RS = 0;// 指令
    LCD_RW = 0;// 写入
    LCD_DataPort = Command;
    LCD_EN = 1;
    LCD_Delay();// 执行需要时间
    LCD_EN = 0;
    LCD_Delay();
}

/**
  * @brief  LCD1602写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void LCD_WriteData(unsigned char Data){
    LCD_RS = 1;// 数据
    LCD_RW = 0;// 写入
    LCD_DataPort = Data;
    LCD_EN = 1;
    LCD_Delay();// 执行需要时间
    LCD_EN = 0;
    LCD_Delay();
}

/**
  * @brief  LCD1602设置光标位置
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @retval 无
  */
void LCD_SetCursor(unsigned char Line,unsigned char Column){
    Column--;
    if(Line == 2) Column |= 0x40;
    Column |= 0x80;
    LCD_WriteCommand(Column);
}

/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD_Init(){
    LCD_WriteCommand(0x38);// 八位数据接口,两行显示,5*7点阵
    LCD_WriteCommand(0x0C);// 显示开,光标关,闪烁关
    LCD_WriteCommand(0x06);// 数据读写操作后,光标自动加一,画面不动
    LCD_WriteCommand(0x01);// 清屏
}

/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char){
    LCD_SetCursor(Line, Column);
    LCD_WriteData(Char);
}

/**
  * @brief  在LCD1602指定位置开始显示所给字符串
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串
  * @retval 无
  */
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String){
    LCD_SetCursor(Line, Column);
    while(*String){
        LCD_WriteData(*String);
        String++;
    }
}

/**
  * @brief  在LCD1602指定位置开始显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~65535
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length) {
    unsigned int K = 1;
    LCD_SetCursor(Line, Column);
    while(--Length) K *= 10;
    while(K){
        LCD_WriteData((Number / K) % 10 + '0');
        K /= 10;
    }
}

/**
  * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-32768~32767
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length){
    LCD_SetCursor(Line, Column);
    if(Number < 0){
        LCD_WriteData('-');
        Number = -Number;
    }else{
        LCD_WriteData('+');
    }
    LCD_ShowNum(Line, Column+1, Number, Length);
}

/**
  * @brief  在LCD1602指定位置开始以十六进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFF
  * @param  Length 要显示数字的长度,范围:1~4
  * @retval 无
  */
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length){
    unsigned int i,k;
    LCD_SetCursor(Line, Column);
    for (i = Length; i >= 1; i--) {
        k = (Number >> ((i-1) * 4)) & 0x000F;
        if(k < 10){
            LCD_WriteData(k + '0');
        }else{
            LCD_WriteData(k - 10 + 'A');
        }
    }
}

/**
  * @brief  在LCD1602指定位置开始以二进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length){
    unsigned int i;
    LCD_SetCursor(Line, Column);
    for (i = Length; i >= 1; i--) {
        LCD_WriteData(((Number >> (i-1))  & 0x0001) + '0');
    }
}

LCD1602.h

#ifndef __LCD1602_H__
#define __LCD1602_H__

void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);

#endif

15-1 直流电机驱动 PWM

PWM(Pulse Width Modulation)即脉冲宽度调制,在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速、开关电源等领域

PWM重要参数:

频率(越高越好 ) = 1 / TS 占空比 = TON / TS 精度 = 占空比变化步距

(瞎眼的PWM就是这么来的!捏妈!)

15-2 LED呼吸灯

主函数实现呼吸灯

#include <REGX52.H>

sbit LED = P2^0;

void Delay(unsigned int t){
    while(t--);
}

int main(){
    unsigned char Time,i,State = 1;
    while(1){
        Time += State;
        if(Time >= 100) State = -1;
        if(Time <= 1) State = 1;
        for(i=0;i<10;i++){
            LED=0;
            Delay(Time);
            LED=1;
            Delay(100 - Time);
        }
    }
}

15-3 直流电机调速

#include <REGX52.H>
#include "Delay.h"
#include "Key.h"
#include "Nixie.h"
#include "Timer0.h"

unsigned char KeyNum, Compare, Speed;

sbit Motor = P1 ^ 0;

int main() {
    Timer0Init();
    
    Nixie(1, 0);

    while (1) {
        KeyNum = Key();
        if (KeyNum) {
            if (KeyNum == 1) {
                Speed++;
                Speed %= 4;
            }
            Nixie(1, Speed);
            switch (Speed) {
                case 0: Compare = 0;break;
                case 1: Compare = 20;break;
                case 2: Compare = 50;break;
                case 3: Compare = 100;break;
            }
            KeyNum = 0;
        }
    }

}

// 中断函数 100us@11.0592HMz
void timer0_Routine() interrupt 1
{
    static unsigned int T0Count,KeyCount;
    static unsigned char K;
    TL0 = 0xA4;		//设置定时初值
    TH0 = 0xFF;		//设置定时初值
    T0Count++;
    if(T0Count % 10 == 0) KeyCount ++;
    if (KeyCount % 20 == 0) {
        K = Key();
        if (K) KeyNum = K;
        KeyCount = 0;
    }
    T0Count %= 100;

    Motor = !(T0Count >= Compare);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值