STM32 IIC通信简介+PCF8563时钟芯片示例

2023-11-02

IIC总线是嵌入式设备最常用的接口之一,包括51单片机在内的MCU一般都可以进行IIC通信。

IIC通信有3种类型的信号:开始信号,结束信号,和应答信号。

开始信号:SCL为高电平,SDA由高电平向低电平跳变,表示可以开始传输信号,进行通信了。

结束信号:SCL为高电平,SDA由低电平向高电平跳变,表示传输信号的时间已经过了。

应答信号:接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。

CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号。这个很容易理解,就好像人的交流(通信),要建立起通信,肯定需要开始条件,好像需要约定两个人都上线了才能通信,这就是开始信号。结束信号也一样,处理器不可能一直处于与其他IC通信的状态的。而应答信号,发送方把自己要发送的数据发送出去了,但不知道对方有没有收到,所以有些情况,需要等待接收方返回应答信号,告诉发送方我已经收到了,你可以继续发送下一条数据。


值得注意的是,虽然大部分MCU都带有IIC总线接口,但实际应用中,使用的一般都是引脚模拟的IIC。


以下分别介绍IIC配置的软件实现。

引脚配置(初始化)

//初始化IIC
void IIC_Init(void)
{					     
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );	
	   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); 	//PB6,PB7 输出高
}

#define SDA_IN()  {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=0X80000000;}
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=0X30000000;}


引脚模拟的IIC开始信号

//产生IIC起始信号
void IIC_Start(void)
{
	SDA_OUT();     //sda线输出
	IIC_SDA=1;	  	  
	IIC_SCL=1;
	delay_us(4);
 	IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
	delay_us(4);
	IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 
}	  


停止信号

//产生IIC停止信号
void IIC_Stop(void)
{
	SDA_OUT();//sda线输出
	IIC_SCL=0;
	IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
 	delay_us(4);
	IIC_SCL=1; 
	IIC_SDA=1;//发送I2C总线结束信号
	delay_us(4);							   	
}

应答信号

//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
u8 IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	SDA_IN();      //SDA设置为输入  
	IIC_SDA=1;delay_us(1);	   
	IIC_SCL=1;delay_us(1);	 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL=0;//时钟输出0 	   
	return 0;  
}

IIC通信发送一个字节的信号、接收一个字节的信号

//产生ACK应答
void IIC_Ack(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=0;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}
//不产生ACK应答		    
void IIC_NAck(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=1;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}					 				     
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答			  
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	SDA_OUT(); 	    
    IIC_SCL=0;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
       
			if((txd&0x80)>>7)
				IIC_SDA=1;
			else
				IIC_SDA=0;
			txd<<=1; 	  
			delay_us(2);  
			IIC_SCL=1;
			delay_us(2); 
			IIC_SCL=0;	
			delay_us(2);
    }	 
} 	    
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
        IIC_SCL=0; 
        delay_us(2);
		IIC_SCL=1;
        receive<<=1;
        if(READ_SDA)receive++;   
		delay_us(1); 
    }					 
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK   
    return receive;
}



IIC接口的IC很多,常见常用的AT24C02,DS1302,PCF8563等都是IIC接口的。

下面以PCF8563为例介绍如何使用MCU的IIC与其他器件进行通信。

PCF8563是一款时钟芯片,具体的介绍请查看手册。时钟芯片大部分都是采用BCD编码的。先介绍一下BCD码和十进制码之间的转换。

可以认为,BCD码就是十进制码变成十六进制。如:59(十进制)对应的BCD码是0x59 。 10 对应0x10 。 1 对应0x01 。


互相转换的代码:

unsigned char RTC_BinToBcd2(unsigned char BINValue)
{
	unsigned char bcdhigh = 0;
	
	while (BINValue >= 10)
	{
		bcdhigh++;
		BINValue -= 10;
	}
	
	return ((unsigned char)(bcdhigh << 4) | BINValue);
}


unsigned char RTC_Bcd2ToBin(unsigned char BCDValue)
{
	unsigned char tmp = 0;
	
	tmp = ((unsigned char)(BCDValue & (unsigned char)0xF0) >> (unsigned char)0x04) * 10;
	return (tmp + (BCDValue & (unsigned char)0x0F));
}


根据手册,查得IIC器件的地址

#define PCF8563_Write                            (unsigned char)0xa2  //写命令
#define PCF8563_Read                             (unsigned char)0xa3  //读命令


向PCF8563的某一寄存器写入某一数据。通信过程也是很明显的,先发出一个开始信号,表示开始传输的是数据了,发送一条读命令,告诉接收方(PCF8563)我要写入数据

然后等待PCF8563回应说我知道了,你可以写入数据了。然后就再告诉对方写入哪里,等他的回应,最后告诉他要写入什么,同样也等待回应。写完了就可以结束这次通信了

void PCF8563_Write_Byte(unsigned char REG_ADD, unsigned char dat)
{
	IIC_Start();
	IIC_Send_Byte(PCF8563_Write);//发送写命令并检查应答位
	while(IIC_Wait_Ack());
	IIC_Send_Byte(REG_ADD);
	IIC_Wait_Ack();
	IIC_Send_Byte(dat);	//发送数据	
	IIC_Wait_Ack();
	IIC_Stop();
} 

unsigned char PCF8563_Read_Byte(unsigned char REG_ADD)
{
	u8 ReData,t=0;
	IIC_Start( );
	IIC_Send_Byte(PCF8563_Write);	//发送写命令并检查应答位
	while(IIC_Wait_Ack( ));
	IIC_Send_Byte(REG_ADD);	//确定要操作的寄存器
	IIC_Wait_Ack();
	IIC_Start();	//重启总线
	IIC_Send_Byte(PCF8563_Read);	//发送读取命令
	IIC_Wait_Ack();
	ReData = IIC_Read_Byte(0);	//读取数据,加发送非应答
	IIC_Stop();
	return ReData;
}

/******************************************************************************
                             参数寄存器地址宏定义
******************************************************************************/

#define PCF8563_Address_Control_Status_1         (unsigned char)0x00  //控制/状态寄存器1
#define PCF8563_Address_Control_Status_2         (unsigned char)0x01  //控制/状态寄存器2

#define PCF8563_Address_CLKOUT                   (unsigned char)0x0d  //CLKOUT频率寄存器
#define PCF8563_Address_Timer                    (unsigned char)0x0e  //定时器控制寄存器
#define PCF8563_Address_Timer_VAL                (unsigned char)0x0f  //定时器倒计数寄存器

#define PCF8563_Address_Years                    (unsigned char)0x08  //年
#define PCF8563_Address_Months                   (unsigned char)0x07  //月
#define PCF8563_Address_Days                     (unsigned char)0x05  //日
#define PCF8563_Address_WeekDays                 (unsigned char)0x06  //星期
#define PCF8563_Address_Hours                    (unsigned char)0x04  //小时
#define PCF8563_Address_Minutes                  (unsigned char)0x03  //分钟
#define PCF8563_Address_Seconds                  (unsigned char)0x02  //秒

#define PCF8563_Alarm_Minutes                    (unsigned char)0x09  //分钟报警
#define PCF8563_Alarm_Hours                      (unsigned char)0x0a  //小时报警
#define PCF8563_Alarm_Days                       (unsigned char)0x0b  //日报警
#define PCF8563_Alarm_WeekDays                   (unsigned char)0x0c  //星期报警

<pre name="code" class="cpp">void pcf_reg_init(void)
{
	_PCF8563_Register_Typedef PCF8563_Register_Structrue;
		
	PCF8563_Register_Structrue.Control_Status_1=0x00;
	PCF8563_Register_Structrue.Control_Status_2=0x02;
	//默认时间设置
	PCF8563_Register_Structrue.Years=0x16;
	PCF8563_Register_Structrue.Months_Century=0x08;
	PCF8563_Register_Structrue.WeekDays =0x01;
	PCF8563_Register_Structrue.Days=0x08;
	PCF8563_Register_Structrue.Hours=0x04;		
	PCF8563_Register_Structrue.Minutes=0x03;
	PCF8563_Register_Structrue.Seconds=0x55|(0<<7);
	//默认闹钟设置
	PCF8563_Register_Structrue.Hour_Alarm=0x03|(0<<7);
	PCF8563_Register_Structrue.Minute_Alarm=0x05|(0<<7);
	PCF8563_Register_Structrue.WeekDays_Alarm=0x01|(1<<7);	
	PCF8563_Register_Structrue.Day_Alarm=0x08|(1<<7);
		
	//定时器默认设置
	PCF8563_Register_Structrue.CLKOUT_Frequency=0x03;
	PCF8563_Register_Structrue.Timer_Countdown_Value=0x00;
	PCF8563_Register_Structrue.Timer_Control=0x03;
		
	PCF8563_SetRegister(PCF_Format_BCD,PCF_Century_20xx,&PCF8563_Register_Structrue);

}


 

其中最后一个函数就是往各个寄存器写入相应的值,这里不再贴代码了。






本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32 IIC通信简介+PCF8563时钟芯片示例 的相关文章

随机推荐

  • 了解MQ和安装使用RabbitMQ

    什么是消息队列 本质是一个队列 队列中出存放的是跨进程的通信机制 用于上下游传递消息 MQ是常见的上下游 逻辑解耦 物理解耦 的消息通信服务 在使用MQ之后 消息发送上只需要依赖MQ 不用依赖其他服务 功能 1 流量削峰 举个例子 系统最多
  • 最新抖音快手小红书西瓜全平台解析接口api开发文档

    简介 从短视频平台APP中复制出来的分享链接 通过接口获取或通过主页在线一键解析获取短视频中的 视频标题 视频封面 无水印视频地址 图集列表等参数信息 接口地址 https eeapi cn 返回格式 JSON 请求方式 GET 客户UId
  • 常见的Java框架有哪些?

    作为一名合格的Java开发工程师 不仅需要了解开发技术 还需要了解清楚Java主流框架信息 那么常见的Java框架有哪些 常见的Java框架有哪些 1 Spring框架 Spring框架是现在Java后端框架家族里面最强大的一个 拥有IOC
  • 【PTA】约瑟夫环问题

    n个小孩围成一圈 从第一个小孩开始从1到m报数 报到m的小孩出列 下一个小孩继续从1开始报数 出列的小孩不参与报数 问小孩的出列顺序 import java util public class Main public static void
  • 【Proteus仿真】【51单片机】简易信号发生器设计

    文章目录 一 主要功能 二 使用方法 三 硬件资源 四 软件设计 1 主要代码 五 实验现象 联系作者 一 主要功能 1 可生成常用波形 方波 锯齿波 三角波 阶梯波 正玄波 2 可通过按键切换不同波形输出 二 使用方法 系统运行后 按下K
  • 如何在git中修改用户名和密码

    随着开源软件的不断发展 git已成为了极其流行的版本控制系统 git是一个非常强大的工具 引入了一系列的概念和机制 便于软件工程师跟踪他们的代码变化 这篇文章将会谈论如何在git中修改用户名和密码 git是什么 Git是一个由Linus T
  • 分治法-Strassen-矩阵乘法详细代码

    public class Matrix 初始化一个随机nxn阶矩阵 public static int initializationMatrix int n int result new int n n for int i 0 i lt n
  • 全网最全Android Framework框架总结,Android如何入门Framework层

    每一个Android开发 基本都了解或者学习过系统的知识 一是因为国内软件行业内卷 找工作时 面试造火箭 工作拧螺丝 的局面导致的 另一方面 从客观角度来讲 学习Android系统 Framework 等源代码或者其工作方式也可以提升我们
  • Nginx系列教程(五)

    冷备份是定期复制 不能保证数据可用性 热备份又分为异步热备和同步热备 异步热备是指 多份数据副本的写入操作异步完成 同步热备是指 多份数据副本的写入操作同时完成 服务层面 失效转移 如某块磁盘损坏 将从备份的磁盘读取数据 首先是已经提前做好
  • 九问国产操作系统,九大掌门人万字回应!

    整理 屠敏 责编 唐小引 出品 CSDN ID CSDNnews 在国产基础软件发展的多年间 众多摸着石头过河的先行者无数次感叹 蜀道之难 难于上青天 不仅包含数据库 中间件 芯片 操作系统等核心软硬件研发 也覆盖了每种技术领域的生态建设
  • 【反序列化漏洞-01】序列化与反序列化概述

    为什么要序列化 百度百科上关于序列化的定义是 将对象的状态信息转换为可以存储或传输的形式 字符串 的过程 在序列化期间 对象将其当前状态写入到临时或持久性存储区 非关系型键值对形式的数据库Redis 与数组类似 以后 可以通过从存储区中读取
  • VMWARE虚拟机网络环境配置

    VMWARE虚拟机网络环境配置 1 查看物理机ip信息 命令 ipconfig 2 配置网络适配器环境 点击 控制面板 选择 网络和Internet 点击 查看网络状态和任务 点击 更改适配器设置 选择VMnet8那个适配器 邮件选择 属性
  • 如何设计好API

    语言首先是写给人看的 只是恰巧 incidentally 能够运行 计算机程序的构造和解释 好的API应对客户端友好 换言之就是能够直接通过其方法签名而理解它做的事情 而不用深入去阅读方法的实现 甚至深入阅读API所在的整个类 单纯的介绍如
  • LeetCode 263. 丑数

    题目链接 https leetcode cn problems ugly number C 代码如下 class Solution public bool isUgly int n if n lt 1 return false while
  • C/C++ rand()产生随机数 模拟 掷骰子 小游戏代码

    1 源代码如下 file 模块名 文件名 tossGame cpp 相关文件 文件实现功能 模拟掷骰子的随即过程 同时掷出2个骰子 如果结果和为7或者11则玩家获胜 如果结果为2则玩家失败 其他结果可以按回车继续 或者输入q结束程序 作者
  • XML格式Word文档表格常用标签

    常用标签 名称 描述 w p 文本段落 w pPr 段落设置 w ind 缩进 w jc 文本位置 left center right w r 文字块 w rPr 文本设置 w rFonts 字体设置 w color 文字颜色 w sz 文
  • 【Qt串口波形绘图】基于QCustomPlot的串口波形绘图上位机,源码开放

    之前写了一款串口调试上位机 有很大的功能欠缺 整体感觉十分不爽 这次在原有基础上进行了优化 并且增加了协议帧捕获功能 使用QCustomPlot进行波形绘图的功能 可应用于实时动态绘图 直男审美 实在不懂UI设计 望吐槽时嘴下留情 GitH
  • 我需要Python代码,构建一个微信小程序

    我可以为你提供一些关于如何构建一个微信小程序的Python代码 你可以使用微信小程序的开发工具 使用Python编写小程序的业务逻辑 并在小程序的前端界面中使用HTML CSS和JavaScript实现界面的设计和交互
  • Linux编程-基础知识

    Linux基础 前言 Linux 目录结构 具体目录结构 Vi 和 Vim 编辑器 Vi 和 Vim 的三种模式 正常模式 插入模式 编辑模式 命令行模式 前言 我使用的环境是在VMware虚拟机 centOS8 Xshell7 Xftp7
  • STM32 IIC通信简介+PCF8563时钟芯片示例

    IIC总线是嵌入式设备最常用的接口之一 包括51单片机在内的MCU一般都可以进行IIC通信 IIC通信有3种类型的信号 开始信号 结束信号 和应答信号 开始信号 SCL为高电平 SDA由高电平向低电平跳变 表示可以开始传输信号 进行通信了