找回密码
 立即注册
楼主: wuzhengmin

第一次:环境的创立 | 送实验箱

[复制链接]
  • 打卡等级:偶尔看看III
  • 打卡总天数:41
  • 最近打卡:2025-10-14 08:27:33
已绑定手机

2

主题

258

回帖

750

积分

高级会员

积分
750
发表于 前天 10:37 | 显示全部楼层
/*  写程序要点  首先定义引脚#define DQ P33  //18b20的引脚
5.1 底层驱动
复位(输出0保持480us,输出1保持60us,读取当前电平,延时420us)写0(输出0保持60us+,输出1保持1us+).
写1(输出0保持1us+,输出1保持60us+)读0/1(输出0保持1us+,,输出1保持Tus+,读取当前电平,延时60us)
5.2 接口函数
写1字节读1字节
5.3 用户功能函数
温度读取换算函数
(复位-CCH-44H-等待-复位-CCH-BEH-读取2字节温度数据-换算)
观察所需要的延时 482us  420us(用480us顶替) 60us  1us  @22.1184MHz
*/


#include "18b20.h"

u16 Temp=0;                        //温度数值
bit MinusFlag;                //温度正负  0:正数  1:负数

//需要的延时 482us  420us(用480us顶替) 60us  1us  @22.1184MHz
void Delay480us()                //@22.1184MHz
{
        unsigned long i;

        i = 2653UL;
        while (i) i--;
}

void Delay60us()                //@22.1184MHz
{
        unsigned long i;

        _nop_();
        _nop_();
        i = 330UL;
        while (i) i--;
}
void Delay1us()                //@22.1184MHz
{
        unsigned long i;

        _nop_();
        i = 4UL;
        while (i) i--;
}

//复位(输出0保持480us,输出1保持60us,读取当前电平,延时420us)
//如果读取到当前电平为高,说明DB18B20未准备好,就继续等待,一直到低电平出现
void DS18b20_Reset(void)                //复位
{
        bit flag = 1;
       
        while( flag )                        //flag先置1进入循环
        {
                DQ = 0;                                //输出低电平
                Delay480us();
                DQ = 1;                                //输出高电平
                Delay60us();
                flag = DQ;                        //读取当前电平
                Delay480us();                //等480us后DQ为0,表示DB18B20已经准备好
        }               
}


和时序图是严格一致的

回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:41
  • 最近打卡:2025-10-14 08:27:33
已绑定手机

2

主题

258

回帖

750

积分

高级会员

积分
750
发表于 前天 10:44 | 显示全部楼层
接着来:


//写逻辑0(输出0保持60us+,输出1保持1us+)
void DS18b20_Write_0(void)                //写逻辑0码
{
        DQ = 0;                                //输出低电平
        Delay60us();
        DQ = 1;                                //输出高电平
        Delay1us();               
        Delay1us();       
}



//写逻辑1(输出0保持1us+,输出1保持60us+)读0/1(输出0保持1us+,,输出1保持Tus+,读取当前电平,延时60us)
void DS18b20_Write_1(void)                //写逻辑1码
{
        DQ = 0;                                //输出低电平
        Delay1us();       
        Delay1us();       
        DQ = 1;                                //输出高电平
        Delay60us();       
}


底层驱动就这3个函数,接下来是5.2 接口函数
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:41
  • 最近打卡:2025-10-14 08:27:33
已绑定手机

2

主题

258

回帖

750

积分

高级会员

积分
750
发表于 前天 11:12 | 显示全部楼层
底层还有读DQ的电平:


//读DQ的状态(0/1)并返回(输出0保持1us+,,输出1保持Tus+,读取当前电平,延时60us)

bit DS18b20_Read(void)                //读取DQ电平
{
        bit state ;
       
        DQ = 0;                                //输出低电平
        Delay1us();       
        Delay1us();       
        DQ = 1;                                //输出低电平
        Delay1us();       
        Delay1us();       
        state = DQ;                        //读取当前电平

        Delay60us();

        return         state;
}



5.2 接口函数  写1字节


//写1字节:例如发送  0XCC  = 1100 1100b & 0x01 C就是12,就是二进制1100
//从低位开始发送,0->,0->,1->,1->,0->,0->,1->,1->
void DS18b20_WriteByte(u8 dat)        //写一个字节
{
        u8 i;
        for( i=0;i<8;i++ )                //循环八次
        {
                if( dat & 0x01 )  //最低位与0x01(取最低位,最低位是1的话与0x01就=1,最低位是0的话与0x01就=0)
                        DS18b20_Write_1();
                else
                        DS18b20_Write_0();
                dat >>=1;       //次低位,再次低位......直到8位完成
        }
}



回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:41
  • 最近打卡:2025-10-14 08:27:33
已绑定手机

2

主题

258

回帖

750

积分

高级会员

积分
750
发表于 前天 11:38 | 显示全部楼层
因为的单总线,上面的void DS18b20_WriteByte(u8 dat)        //写一个字节


每次是输出0码,或者输出1码,8个码构成一个字节


//读1字节:例如我们读的是1000 0000 ,右移一位第二次读的放在高位,上次的高位变第二位
// 1000 0000
// x100 0000 再右移一位第三次读的放在高位,上次的高位变第二位
// xx10 0000 继续搞8次
u8 DS18b20_ReadByte(void)                //读取一个字节
{
        u8 i;
        u8 dat=0;  //这里一定先赋值0
        for( i=0;i<8;i++ )                //循环八次
        {
                dat >>=1;           //开始是0,右移还是0
               
                if( DS18b20_Read() )        //如果读取到的是1,和0x80相或(自然最高位是1)
                        dat |= 0x80;    //否则反正是0,直接搞下一位
        }
        return dat;      //把读到的一个字节数据返回去
}



回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:41
  • 最近打卡:2025-10-14 08:27:33
已绑定手机

2

主题

258

回帖

750

积分

高级会员

积分
750
发表于 前天 12:25 | 显示全部楼层

5.3 用户功能函数
温度读取换算函数

(复位-CCH-44H-等待-复位-CCH-BEH-读取2字节温度数据-换算)
//(复位-CCH-44H-等待-复位-CCH-BEH-读取2字节温度数据-换算)

u16 DS18b20_ReadTemp(void)        //读取并且换算温度,并复制给Temp返回主函数用
{
        u8 TempH;
        u8 TempL;
        u16 temperture;
       
        DS18b20_Reset();                //1.复位
        DS18b20_WriteByte(0XCC);//2.跳过ROM指令
        DS18b20_WriteByte(0X44);//3.开始转化
        while(!DQ);                                //4.等待DQ变成高电平,高电平才能离开执行下一步
                                                        //开始转化引脚DQ一直是低电平,转换结束后才变高电平,才往下执行
        DS18b20_Reset();                //5.复位
        DS18b20_WriteByte(0XCC);//6.跳过ROM指令       
        DS18b20_WriteByte(0XBE);//7.读取2字节的温度数值

        TempL = DS18b20_ReadByte();  //先读出的是低字节,因为低位在前,高位在后
        TempH = DS18b20_ReadByte();       
       
        if( TempH & 0XF8 )                                                        //有1出现就是负数,负数前5位是1111 1000,就是0xF8
        {                                                                                        //&是位与运算符,而&&是逻辑与运算符,&运算符会对两
                                                                                                //个操作数进行按位与运算,而&&运算符只有在两个操作
                                                                                                //数都为非零值时才返回真(1),否则返回假(0)。
/*        if((Temp_Value[1]&0XF8)==0XF8)这条语句用于判断DS18B20传感器读取的温度值是否为零下。具体来说,
Temp_Value[1]数组的第2个元素中,如果高5位全为1(即二进制的11111000,对应的十六进制是0xF8),则表示
读取的温度值为零下。这是因为DS18B20的设计使得其温度数据的符号位由最高几位决定,当这些位均为1时,表
示温度为负。TempH & 0XF8 是简略写法,实际是(TempH&0XF8)==0XF8

*/               
               
                MinusFlag = 1;                                                        //标志位负数
                temperture = ((TempH<<8) | TempL);                //将温度换算成16位
                temperture = (~temperture) +1;                         //按位取反+1
                temperture = temperture*10*0.0625;                //最终温度保留一位小数,本来是小数,u16是整数我们处理整数
        }                                                                                        //方便,所以放大10倍,保留1位小数
        else
        {
                MinusFlag = 0;                                                        //标志位正数
                temperture = ((TempH<<8) | TempL);                //将温度换算成16位,是12位的精度,最终值=乘以0.0625
                temperture = temperture*10*0.0625;                //最终温度保留一位小数,本来是小数,u16是整数我们处理整数
        }                                                                                        //方便,所以放大10倍,保留1位小数
        Temp = temperture;
        return temperture;                                                        //保留一位小数的温度
}

回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:41
  • 最近打卡:2025-10-14 08:27:33
已绑定手机

2

主题

258

回帖

750

积分

高级会员

积分
750
发表于 前天 12:30 | 显示全部楼层
看看主函数:


void main()                                        //程序开始运行的入口
{
        int dat = 0;                        //初始数值
        sys_init();                                //USB功能+IO口初始化
        usb_init();                                //usb库初始化
        EUSB = 1;
        Timer0_Init();                        //定时器0初始化

        EA = 1;                                        //CPU开放中断,打开总中断。

        while( DeviceState != DEVSTATE_CONFIGURED );


       
        while(1)                //死循环
        {
                delay_ms(2);

                if( bUsbOutReady )                                                               
                {
//                        USB_SendData(UsbOutBuffer,OutNumber);
                        usb_OUT_done();
                }

                if( TIM_10MS_Flag==1 )                                                                        //如果10ms到了
                {
                        TIM_10MS_Flag = 0;                                                                        //清空标志位       
                       
                        dat++;
                        if( dat>=30 )        //时间= 30*10ms
                        {
                                dat = 0;
                                DS18b20_ReadTemp();                                                                //300ms到达,读取一次温度
                                if( MinusFlag==1 )
                                {
                                        printf( "当前温度:-%.01f\r\n",(float)((float)Temp/10) );
                                }
                                else
                                        printf( "当前温度:%.01f\r\n",(float)((float)Temp/10) );
                        }
                }


                       
        }
}


回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:41
  • 最近打卡:2025-10-14 08:27:33
已绑定手机

2

主题

258

回帖

750

积分

高级会员

积分
750
发表于 前天 12:51 | 显示全部楼层
最终我们下载烧录,看串口打印的结果是正常的

顺利完成本实验
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:41
  • 最近打卡:2025-10-14 08:27:33
已绑定手机

2

主题

258

回帖

750

积分

高级会员

积分
750
发表于 昨天 19:39 | 显示全部楼层
今天我们继续学习冲哥的3STC32位单片机    第二十七集:软件模拟SPI


SPI是串行外设接口(Serial Peripheral Interface)的缩写。SPI是一种高口速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议。




产生时钟的一侧称为主机,另一侧称为从机。同一时间只有一个主机,但是可以有多个从机。

截图202510131939253028.jpg
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:41
  • 最近打卡:2025-10-14 08:27:33
已绑定手机

2

主题

258

回帖

750

积分

高级会员

积分
750
发表于 昨天 19:41 | 显示全部楼层
SPI总线包括4条逻辑线,定义如下:
MISO:Master input slave output 主机输入,从机输出(数据来自从机);MOSI:Masteroutput slave input主机输出,从机输入(数据来自主机);SCLK:Serial Clock串行时钟信号,由主机产生发送给从机;SS Slave Select 片选信号,由主机发送以控制与哪个从机通信,通常是低电平有效信号

21.3
SPI通信方式

SPI的通信方式通常有3种:单主单从(一个主机设备连接一个从机设备)、互为主从(两个设备连接,设备和互为主机和从机)、单主多从(一个主机设备连接多个从机设备)



截图202510131941479013.jpg
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:41
  • 最近打卡:2025-10-14 08:27:33
已绑定手机

2

主题

258

回帖

750

积分

高级会员

积分
750
发表于 昨天 19:43 | 显示全部楼层
多数是单主多从,由SS的电平决定每一时刻和谁通信
SPI数据模式:
时钟极性(CKP/CPOL)

根据硬件制造商的命名规则不同,时钟极性通常写为CKP或CPOLCKOL可以配置为1或0。这意味着可以根据需要将时钟的默认状态(IDLE)设置为高或低。当然具体的配置必须参考设备的数据手册正确设置CKP=0:时钟空闲IDLE为低电平 0;CKP=.1:时钟空闲IDLE为高电平·1;
时钟相位(CKE/CPHA)

顾名思义,时钟相位/边沿,也就是采集数据时是在时钟信号的具体相位或者边沿;CPHA = 0:在时钟信号SCK的第一个跳变沿采样;CPHA= 1:在时钟信号SCK的第二个跳变沿采样;时钟极性和相位共同决定读取数据的方式,比如信号上升沿读取数据还是信号下降沿读取数据;


截图202510131942302383.jpg
截图202510131943247914.jpg
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-10-14 10:37 , Processed in 0.159366 second(s), 88 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表