当前位置: 首页 > article >正文

Zynq之IIC使用示例

前言

明确设计思路,精准定位问题,对于我们后期理解迭代工程有很大的帮助。

这就是我们常说的40%设计,20%编写和剩下的40%时间进行调试优化

今天为大家带来的是Zynq-PS端的IIC使用demo,通过驱动外设DS1337来强化对IIC的使用方法

问题

Q1:PS端的IIC操作和PL端有何不同?

Q2:XIicPs_MasterSendPolled函数的第三个参数为何赋值ByteCount + 1,是否有影响?

Q3:对比PL端的从机地址,为何XIicPs_MasterSendPolled的第四个参数7bit对应不上?

IIC Controller

PL IIC Logic

在了解Zynq IIC总线前,我们先回顾一下PL端在对IIC总线进行逻辑操作时的执行步骤:

在这里插入图片描述

【PL IIC Logic】:需要说明的是具体的时序由外设IIC时序确定,比如有的外设支持高低两字节存储地址;应答ACK为低电平,未应答NACK为高电平;PL端通过编写代码逻辑实现通信功能

PS IIC Logic

IIC示例有Polled模式和中断两种模式进行逻辑操作,不同于PL端需要编写相应的逻辑代码驱动IIC外设,PS端需要了解对应的寄存器含义,通过封装好的IIC函数进行寄存器配置即可。

在这里插入图片描述

Zynq的PS自带两个IIC控制器,IIC的IO可以映射到MIO或EMIO上。Zynq的IIC控制器支持Master模式或者Slave模式,其中Slave接口通信为APB总线。

本次示例使用的Polled、Master模式,不涉及中断模式相关的寄存器配置说明,感兴趣的伙伴可以前往UG585进行学习,接下来是一些主要寄存器的介绍;

Module NameIIC Controller
Software NameXIICPS
Base Address0xE0004000 IIC0
0xE0005000 IIC1
DescriptionInter Integrated Circuit (IIC)
Vendor InfoCadence IIC

Register NameAddressWidthTypeReset ValueDescription
XIICPS_CR_OFFSET0x0000000016mixed0x00000000Control Register
XIICPS_SR_OFFSET0x0000000416ro0x00000000Status register
XIICPS_ADDR_OFFSET0x0000000816mixed0x00000000IIC Address register
XIICPS_DATA_OFFSET0x0000000C16mixed0x00000000IIC data register
XIICPS_TRANS_SIZE_OFFSET0x000000148rw0x00000000Transfer Size Register

Register XIICPS_CR_OFFSET

Field NameBitsTypeReset ValueDescription
XIICPS_CR_DIV_A_MASK
(DIV_A)
15:14rw0x0Divisor for stage A clock divider.
0 - 3: Divides the input pclk frequency by divisor_a + 1.
XIICPS_CR_DIV_B_MASK
(DIV_B)
13:8rw0x0Divisor for stage B clock divider.
0 - 63 : Divides the output frequency from divisor_a by divisor_b + 1.
reserved7ro0x0Reserved, read as zero, ignored on write.
XIICPS_CR_CLR_FIFO_MASK
(CLR_FIFO)
6rw0x01 - initializes the FIFO to all zeros and clears the transfer size register except in master receive mode. Automatically gets cleared on the next APB clock after being set.
XIICPS_CR_SLVMON_MASK
(SLVMON)
5rw0x0Slave monitor mode
1 - monitor mode.
0 - normal operation.
XIICPS_CR_HOLD_MASK
(HOLD)
4rw0x0hold_bus
1 - when no more data is available for transmit or no more data can be received, hold the sclk line low until serviced by the host.
0 - allow the transfer to terminate as soon as all the data has been transmitted or received.
XIICPS_CR_ACKEN_MASK
(ACKEN)
3rw0x0This bit needs to be set to 1
1 - acknowledge enabled, ACK transmitted
0 - acknowledge disabled, NACK transmitted.
XIICPS_CR_NEA_MASK
(NEA)
2rw0x0Addressing mode: This bit is used in master mode only.
1 - normal (7-bit) address
0 - reserved
XIICPS_CR_MS_MASK
(MS)
1rw0x0Overall interface mode:
1 - master
0 - slave
XIICPS_CR_RD_WR_MASK
(RD_WR)
0rw0x0Direction of transfer:
This bit is used in master mode only.
1 - master receiver
0 - master transmitter.

通过上述寄存器字段可以看出主要是关于IIC总线通信状态的一些配置,接下来我们将针对DIV分频和HOLD拉伸字段进行解释;

XIICPS_CR_DIV_MASK

在Master模式下,时钟启用用于建立生成期望的SCL频率

在这里插入图片描述

I I C   S C L   C l o c k = C P U ‾ 1 X ‾ C l o c k   /   ( 22 × ( d i v i s o r ‾ a + 1 ) × ( d i v i s o r ‾ b + 1 ) ) IIC \ SCL \ Clock = CPU\underline{}1X\underline{}Clock \ / \ (22 \times (divisor\underline{}a + 1) \times (divisor\underline{}b + 1)) IIC SCL Clock=CPU1XClock / (22×(divisora+1)×(divisorb+1))
以下列出了标准和高速SCL时钟的计算值:

IIC SCL ClockCPU_1X_Clockdivisor_adivisor_b
100KHz111MHz216
400KHz111MHz012
100KHz133MHz060
400KHz133MHz24
100KHz166MHz316
XIICPS_CR_HOLD_MASK

时钟拉伸则是slave 在master 释放SCL 后,将SCL 主动拉低并保持,此时要求master 停止在SCL 上产生脉冲以及在SDA 上发送数据,直到slave 释放SCL(SCL 为高电平)。之后,master 便可以继续正常的数据传输了。
如果系统中存在这种低速slave 并且slave 实现了clock stretching,则master 必须实现为能够处理这种情况,实际上大部分slave 设备中不包含SCL 驱动器的,因此无法拉伸时钟。

在这里插入图片描述

Register XIICPS_SR_OFFSET

Field NameBitsTypeReset ValueDescription
reserved15:9ro0x0Reserved, read as zero, ignored on write.
XIICPS_SR_BA_MASK
(BA)
8ro0x0Bus Active
1 - ongoing transfer on the I2C bus.
XIICPS_SR_RXOVF_MASK
(RXOVF)
7ro0x0Receiver Overflow
1 - This bit is set whenever FIFO is full and a new byte is received. The new byte is not acknowledged and contents of the FIFO remains unchanged.
XIICPS_SR_TXDV_MASK
(TXDV)
6ro0x0Transmit Data Valid - SW should not use this to determine data completion, it is the RAW value on the interface.
Please use COMP in the ISR.
1 - still a byte of data to be transmitted by the interface.
XIICPS_SR_RXDV_MASK
(RXDV)
5ro0x0Receiver Data Valid
1 -valid, new data to be read from the interface.
reserved4ro0x0Reserved, read as zero, ignored on write.
XIICPS_SR_RXRW_MASK
(RXRW)
3ro0x0RX read_write
1 - mode of the transmission received from a master.
reserved2:0ro0x0Reserved, read as zero, ignored on write.

Register XIICPS_ADDR_OFFSET

Field NameBitsTypeReset ValueDescription
reserved15:10ro0x0Reserved, read as zero, ignored on write.
XIICPS_ADDR_MASK
(MASK)
9:0rw0x0Address
0 - 1024: Normal addressing mode uses add[6:0]. Extended addressing mode uses add[9:0].

Register XIICPS_DATA_OFFSET

Field NameBitsTypeReset ValueDescription
reserved15:8ro0x0
XIICPS_DATA_MASK
(MASK)
7:0rw0x0data
0 -255: When written to, the data register sets data to transmit. When read from, the data register reads the last received byte of data.

Register XIICPS_TRANS_SIZE_OFFSET

Field NameBitsTypeReset ValueDescription
XIICPS_TRANS_SIZE_MASK
(MASK)
7:0rw0x0Transfer Size
0-255

该寄存器字段与FIFO传输有所关联

在这里插入图片描述

DS1337 Serial Real-Time Clock

DS1337 Basic Description

DS1337 串行实时时钟是一款低功耗时钟/日历,具有两个可编程时钟闹钟和一个可编程方波输出。地址和数据通过 2 线双向总线串行传输。时钟/日历提供秒、分、时、日、日期、月和年信息。对于少于 31 天的月份,月底的日期会自动调整,包括闰年的更正。时钟以 24 小时或 12 小时格式运行,并带有 AM/PM 指示器。

以下是DS1337的典型工作电路:

在这里插入图片描述

DS1337 Registers Map

以下是DS1337驱动使用需要配置的寄存器,本示例使用DS1337的时钟功能,只需关注00H-06H寄存器即可;

在这里插入图片描述

DS1377读写模式

Data Write: Slave Receiver Mode

在这里插入图片描述

Data Read: Slave Transmitter Mode

在这里插入图片描述

程序分析

main()

extern XIicPs I2cInst0;
extern DS1337_TIME rtc;

/*
typedef struct {
	u8 second;
	u8 minute;
	u8 hour;
	u8 dayOfWeek;// day of week, 1 = Monday
	u8 dayOfMonth;
	u8 month;
	u16 year;
} DS1337_TIME;
*/

void setup()
{
    DS1337_startClock();		// 写秒数并启动时钟
    DS1337_fillByYMD(2019,5,10);// 赋值类型为DS1337_TIME的结构体rtc字段-年月日:May 10,2019
    DS1337_fillByHMS(11,20,30);	// 赋值类型为DS1337_TIME的结构体rtc字段-时分秒:11:20:30"
    DS1337_fillDayOfWeek(FRI);	// 赋值类型为DS1337_TIME的结构体rtc字段-dayofWeek:Friday
    DS1337_setTime();			// write time to the RTC chip

}

int main(void)
{

    i2cps_init(&I2cInst0,IIC_DEVICE_ID0);	// 初始化IIC
	setup();
	while(1)
	{

    DS1337_getTime();	// 获取当前DS1337的时间
	xil_printf("%d:%d:%d-%d-%d-%d-",rtc.hour,rtc.minute,rtc.second,rtc.month,rtc.dayOfMonth,rtc.year+2000);

	switch (rtc.dayOfWeek)// Friendly printout the weekday
	{
		case MON:
			xil_printf("MON");
		  break;
		case TUE:
			xil_printf("TUE");
		  break;
		case WED:
			xil_printf("WED");
		  break;
		case THU:
			xil_printf("THU");
		  break;
		case FRI:
			xil_printf("FRI");
		  break;
		case SAT:
			xil_printf("SAT");
		  break;
		case SUN:
			xil_printf("SUN");
		  break;
	}

	xil_printf("\r\n");

	sleep(1);
	}
    return 0;
}

DS1337_setTime()

DS1337_TIME rtc;
I2C_ADDR8 *DS1337_WBUF = (void*)0x08000000;
I2C_ADDR8 *DS1337_RBUF = (void*)0x08100000;

/*
typedef struct
{
	u8   reg_addr[1];
	u8   reg_buf [2048];
}I2C_ADDR8;
*/

// 设置DS1337的初始化时间为11:20:30-5-10-2019-FRI
void DS1337_setTime()
{
	DS1337_WBUF->reg_addr[0]=0x00;
	DS1337_WBUF->reg_buf[0]=DS1337_decToBcd(rtc.second);// 0 to bit 7 starts the clock
	DS1337_WBUF->reg_buf[1]=DS1337_decToBcd(rtc.minute);
	DS1337_WBUF->reg_buf[2]=DS1337_decToBcd(rtc.hour);// If you want 12 hour am/pm you need to set bit 6
	DS1337_WBUF->reg_buf[3]=DS1337_decToBcd(rtc.dayOfWeek);
	DS1337_WBUF->reg_buf[4]=DS1337_decToBcd(rtc.dayOfMonth);
	DS1337_WBUF->reg_buf[5]=DS1337_decToBcd(rtc.month);
	DS1337_WBUF->reg_buf[6]=DS1337_decToBcd(rtc.year);
    i2cps_wr8(&I2cInst0,DS1337_WBUF,7,DS1337_I2C_ADDRESS);	// DS1337_I2C_ADDRESS为0x68
}

/*
void i2cps_wr8(XIicPs *I2C_Ptr, I2C_ADDR8 *MsgPtr , u16 byte_cnt ,u16 slave_addr)
{

    XIicPs_MasterSendPolled(I2C_Ptr, (u8 *)MsgPtr, byte_cnt + 1, slave_addr);

    while (XIicPs_BusIsBusy(I2C_Ptr)) ;
}
*/

在这里插入图片描述

从main()程序可知,上板调试DS1337_SeTime()函数之前,已经将rtc时分秒、年月日设置为11:20:30-5-10-2019-FRI


通过前面的DS1337读写模式可知该IIC外设器件地址为1101000,最终通过Register XIIPS_ADDR_OFFSET进行器件地址传输,由描述可知对应低7位[6:0],因此i2cps_wr8函数中的第四个参数为0_110_1000即0x68

XIicPs_MasterSendPolled()

/*
typedef struct {
	XIicPs_Config Config;	// < Configuration structure
	u32 IsReady;		// Device is initialized and ready 
	u32 Options;		// Options set in the device 

	u8 *SendBufferPtr;	// Pointer to send buffer 
	u8 *RecvBufferPtr;	// Pointer to recv buffer 
	s32 SendByteCount;	// Number of bytes still expected to send 
	s32 RecvByteCount;	// Number of bytes still expected to receive 
	s32 CurrByteCount;	// No. of bytes expected in current transfer 

	s32 UpdateTxSize;	// If tx size register has to be updated 
	s32 IsSend;		// Whether master is sending or receiving 
	s32 IsRepeatedStart;	// Indicates if user set repeated start 
	s32 Is10BitAddr;	// Indicates if user set 10 bit address 

	XIicPs_IntrHandler StatusHandler;  // Event handler function 
#if defined  (XCLOCKING)
	u32 IsClkEnabled;	// Input clock enabled 
#endif
	void *CallBackRef;	// Callback reference for event handler 
} XIicPs;
*/

s32 XIicPs_MasterSendPolled(XIicPs *InstancePtr, u8 *MsgPtr,
		 s32 ByteCount, u16 SlaveAddr)
{
	u32 IntrStatusReg;
	u32 StatusReg;
	u32 BaseAddr;
	u32 Intrs;
	s32 Status = (s32)XST_FAILURE;
	u32 timeout = 0;
	_Bool Value;

	/*
	 * Assert validates the input arguments.
	 */
	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(MsgPtr != NULL);
	Xil_AssertNonvoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY);
	Xil_AssertNonvoid((u16)XIICPS_ADDR_MASK >= SlaveAddr);

#if defined  (XCLOCKING)	// #if后逻辑未执行,未定义XCLOCKING
	if (InstancePtr->IsClkEnabled == 0) {
		Xil_ClockEnable(InstancePtr->Config.RefClk);
		InstancePtr->IsClkEnabled = 1;
	}
#endif

	BaseAddr = InstancePtr->Config.BaseAddress;    // 赋值已经初始化的InstancePtr中BaseAddress 0xE0004000
	InstancePtr->SendBufferPtr = MsgPtr;	// 赋值DS1337_WBUF指针的地址0x08000000
	InstancePtr->SendByteCount = ByteCount; // 赋值第三个参数byte_cnt + 1即8,这里的+1是因为写入IIC需要确定写入IIC外设寄存器的初始地址,该示例中即DS1337的00H

    // 写入XIICPS_CR_OFFSET-XIICPS_CR_HOLD_MASK字段,Master控制IIC总线
	if (((InstancePtr->IsRepeatedStart) != 0) ||
		(ByteCount > XIICPS_FIFO_DEPTH)) {
		XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET,
				XIicPs_ReadReg(BaseAddr, (u32)XIICPS_CR_OFFSET) |
						(u32)XIICPS_CR_HOLD_MASK);
	}

	(void)XIicPs_SetupMaster(InstancePtr, SENDING_ROLE);	// This function prepares a device to transfers as a master.

	/*
	 * Intrs keeps all the error-related interrupts.
	 */
	Intrs = (u32)XIICPS_IXR_ARB_LOST_MASK | (u32)XIICPS_IXR_TX_OVR_MASK |
		(u32)XIICPS_IXR_NACK_MASK;

	/*
	 * Clear the interrupt status register before use it to monitor.
	 */
	IntrStatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_ISR_OFFSET);
	XIicPs_WriteReg(BaseAddr, XIICPS_ISR_OFFSET, IntrStatusReg);

	/*
	 * Transmit first FIFO full of data.
	 */
	(void)TransmitFifoFill(InstancePtr);

	...... // 后续代码不再展示,都是一些类似的功能函数,感兴趣的伙伴可以查看XIicPs_MasterSendPolled函数进行查看
}

TransmitFifoFill()

s32 TransmitFifoFill(XIicPs *InstancePtr)
{
	u8 AvailBytes;
	s32 LoopCnt;
	s32 NumBytesToSend;

	/*
	 * Determine number of bytes to write to FIFO.
	 */
	AvailBytes = (u8)XIICPS_FIFO_DEPTH -
		(u8)XIicPs_ReadReg(InstancePtr->Config.BaseAddress,
					   XIICPS_TRANS_SIZE_OFFSET);
	
    // 判断将要发送的字节数量是否小于写FIFO可用字节量,可跳转至目录Register XIICPS_TRANS_SIZE_OFFSET框图进行查看
	if (InstancePtr->SendByteCount > (s32)AvailBytes) {
		NumBytesToSend = (s32)AvailBytes;
	} else {
		NumBytesToSend = InstancePtr->SendByteCount;
	}

	/*
	 * Fill FIFO with amount determined above.
	 */
	for (LoopCnt = 0; LoopCnt < NumBytesToSend; LoopCnt++) {
		XIicPs_SendByte(InstancePtr);
	}

	return InstancePtr->SendByteCount;
}

XIicPs_SendByte()

#define XIicPs_SendByte(InstancePtr)					\
{									\
	u8 Data;							\
	Data = *((InstancePtr)->SendBufferPtr);				\
	 XIicPs_Out32((InstancePtr)->Config.BaseAddress			\
			 + (u32)(XIICPS_DATA_OFFSET), 			\
					(u32)(Data));			\
	(InstancePtr)->SendBufferPtr += 1;				\
	(InstancePtr)->SendByteCount -= 1;\
}

由上述代码可知,依次将DS1337_WBUF指针指向的reg_addr和reg_buf填充至FIFO

*SendBuffPtrData -> FIFOSendByteCount
0x080000000x00(要写入DS1337的初始寄存器地址)8
0x080000010x30(Send)7
0x080000020x20(minute)6
0x080000030x11(hour)5
0x080000040x05(dayOfWeek)4
0x080000050x10(dayOfMonth)3
0x080000060x05(month)2
0x080000070x19(year)1

结果展示

经过一系列函数操作后,驱动DS1337并初始化设置为11:20:30-5-10-2019-FRI,后DS1337内部自动计时,PS端通过IIC与DS1337通信,间隔1s读取时间并通过串口打印信息

在这里插入图片描述

参考

  • Zynq 7000 SoC Technical Reference Manual(UG585)
  • 米联客ZynqSocSDK入门篇-IIC-RTC实验
  • DS1337 Serial Real-Time Clock

http://www.kler.cn/a/286751.html

相关文章:

  • ES6语法
  • ThinkPHP 8模型与数据的插入、更新、删除
  • uniapp+Vue3(<script setup lang=“ts“>)模拟12306城市左右切换动画效果
  • 【Elasticsearch】腾讯云安装Elasticsearch
  • C++ STL(8)map
  • 免费下载 | 2024中国智算中心产业发展白皮书
  • Multisim 仿真入门
  • (已开源-CVPR 2024)YOLO-World: Real-Time Open-Vocabulary Object Detection
  • 以基于Prometheus和Grafana的现代服务器监控体系构建
  • Flutter中组件动态可见的实现
  • Java 入门指南:Java 并发编程 —— AQS、AQLS、AOS 锁与同步器的框架
  • 智能报警物联网系统:使用MQTT和与Grafana集成的InfluxDB监控工地电梯流量和气象数据
  • 用AI生成旅游打卡照!FLUX假装去旅行lora的使用【附工作流】
  • flutter封装Dio使用
  • 计算机毕业设计选题推荐-医疗就诊平台-在线医疗问诊系统-Java/Python项目实战
  • 【硬件操作入门】3--同步与异步、半双工传输、UART硬件介绍、bps速率计算
  • linux 文件管理命令
  • 【Tools】二叉树中序遍历
  • DOM 方法:深入解析与实用指南
  • 静态库和共享库
  • 聚焦AI4SE软件工程领域,基于Multi Agent System多智能体系统开发的最新成果,实现软件开发领域的PUGC!
  • 【Python】简单的爬虫抓取
  • 前端配置环境
  • USB3202N多功能数据采集卡16位模拟量250K频率LabVIEW采集卡
  • 音视频-图像篇(YUV和RGB)
  • Django1.5自定义User模型