⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sndsiic.c

📁 4510b的vxworks的BSP
💻 C
字号:
/* sndsIIC.c - SNDS IIC driver */

/* Copyright 1984-1997 Wind River Systems, Inc. */
#include "copyright_wrs.h"

/*
modification history
--------------------
01a,20dec99,ka  written
*/

/*
DESCRIPTION

This module implements the IIC driver for Samsung SNDS100 Ver 1.0 Evaluation 
Board for their KS32C50100 microcontroller.

KS32C50100 is an ARM based processor with several integrated peripherals.
It has an interrupt controller, two 32-bit timers, one Ethernet controller,
two HDLC controllers, one IIC controller, general purpose I/O ports, and a 
2-channel DMA controller.

The built-in IIC controller supports master mode only.  The SNDS100 board has
only serial EEPROM on the IIC bus.  Hence this driver is used for accessing
the serial EEPROM. The slave address of the EEPROM is 0xA0.

The driver uses two binary semaphores, one for transmit and one for receive
operations.  The interrupt service routines give the semaphores and the main
routines take the semaphores.  A mutual exclusion semaphore is used to avoid
multiple entries to the driver routines.

BOARD LAYOUT
This device is on-chip.  No jumpering diagram is necessary.

EXTERNAL INTERFACE
sndsIICDevInit() function is called from syHwInit() function.  At this time
VxWorks kernel is not initialized.  Hence creation of semaphores cannot be done
at this time.  Accordingly, sndsIICDevInit2() function has to be called from 
sysHwInt2()function.

sndsIICWrite() and sndsIICRead() functions are called for write and read
operations respectively.  sndsIICIoctl() function can be called to change the
IIC prescaler value.

SYSTEM RESOURCE USAGE
When implemented, this driver requires the following system resources:

    - one mutual exclusion semaphore
    - two binary semaphores
	- one interrupt vector and two interrupt service routines

No dynamic memory allocation is done by this driver.

INCLUDES:
sndsIIC.h
*/

/* includes */

#include "sndsIIC.h"

/* Local data structures */

LOCAL SNDS_IIC sndsIIC;

/* forward static declarations */
LOCAL UINT32 setPreScaler(UINT32 sclk);
LOCAL void sndsIICWriteISR();
LOCAL void sndsIICReadISR();

/*******************************************************************************
*
* sndsIICDevInit - initialize the IIC driver
*
* This routine initializes the IIC driver and the device to the operational state.
*
* RETURNS: N/A
*/
void sndsIICDevInit()
	{
	UINT32 nPreScaleValue;

	sndsIIC.int_vec = INT_LVL_IIC;
	sndsIIC.iiccon = (UINT32 *)SNDS_IICCON;
	sndsIIC.iicbuf = (UINT32 *)SNDS_IICBUF;
	sndsIIC.iicps = (UINT32 *)SNDS_IICPS;
	sndsIIC.iiccnt = (UINT32 *)SNDS_IICCNT;

	/* Reset the IICCON Registers */
	SNDS_IIC_REG_WRITE (iiccon, SNDS_IIC_RESET_IICCON);

	/* Set the Prescaler value */
	nPreScaleValue = setPreScaler(SCLK);
	SNDS_IIC_REG_WRITE (iicps, nPreScaleValue);
	}

/*******************************************************************************
*
* sndsIICDevInit2 - Initialize the semaphores used by IIC driver
*
* This routine creates the semaphores used by the IIC driver.  This function can
* be called only after the kernel is initialized, preferrably from sysHwInit2()
*
* RETURNS: N/A
*/
void sndsIICDevInit2()
	{
	/* Create mutual semaphore to guard the global SNDS_IIC structure */
	sndsIIC.IICSemID = semMCreate (SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
	
	/* Synchronisation semaphores */
	sndsIIC.IIC_Txmit.endofWriteSemID = semBCreate (SEM_Q_FIFO, SEM_EMPTY); 
	sndsIIC.IIC_Rcv.endofReadSemID = semBCreate (SEM_Q_FIFO, SEM_EMPTY); 
	}

/*******************************************************************************
*
* sndsIICWrite - Write data into the IIC device
*
* This routine writes the data given by the application into the IIC device.
*
* RETURNS: OK if write is successful, else ERROR
*/
STATUS sndsIICWrite (UINT8 slaveAddr, UINT16 writeAddr, UINT8 *data, UINT32 sizeOfData)
	{
	UINT32 page = 0;
	UINT32 numPages = 0;
	UINT32 remain_byte = 0;
	UINT16 pageAccessAddr = 0;
	UINT8 bEndWrite = 0;
	UINT32 status;
	STATUS nRetVal = OK;
	UINT32 j=0;

	/* Validation of Input Parameters */
	if (slaveAddr != EEPROM_SLAVE_ADDR)	/* At present, only EEPROM is supported on IIC bus on SNDS board */
		return(ERROR);
	if (writeAddr >= EEPROM_MAX_SIZE)
		return(ERROR);
	if (data == NULL)		
		return(ERROR);
	
	/* Connect the IIC Bus interrupt to WriteISR */
   	if (intConnect (INUM_TO_IVEC (sndsIIC.int_vec), sndsIICWriteISR, NULL) == ERROR)
		return(ERROR);
   	if (intEnable (sndsIIC.int_vec) == ERROR) /*Enable the IIC bus interrupt */
		return(ERROR);

	pageAccessAddr = writeAddr;
	sndsIIC.IIC_Txmit.slaveAddress = slaveAddr;
	sndsIIC.IIC_Txmit.totalBytesWritten = 0;

	/* semTake */
	if (semTake (sndsIIC.IICSemID, WAIT_FOREVER) == ERROR)
		return(ERROR);

	if ((sizeOfData % EEPROM_PAGE_SIZE) != 0)				/* Calculate no of page write operations */
		numPages = (sizeOfData / EEPROM_PAGE_SIZE) + 1;
	else
		numPages = sizeOfData / EEPROM_PAGE_SIZE;

	remain_byte = (UINT32)(sizeOfData % EEPROM_PAGE_SIZE); /* Calculate size of data in last page */
	if (!remain_byte)
		remain_byte = EEPROM_PAGE_SIZE;

	/* write the data page by page with 20ms delay provided for internal write operation by 
	 * the slave device after each page
	 */
	for (page = 1; (page <= numPages) && (bEndWrite == 0); page++)
		{
		if (pageAccessAddr > EEPROM_MAX_SIZE)
			{
			break;
			}

		if (sizeOfData < EEPROM_PAGE_SIZE) /* if only one page to be written */
			{
			for(j = 0; j < sizeOfData; j++)	
				sndsIIC.IIC_Txmit.pageBuff[j] = *data++;
			sndsIIC.IIC_Txmit.dataSize = sizeOfData;

			/* If (current write pointer address + no. of data) > max offset, 
			 * omit out of range data 
			 */
			if ((sndsIIC.IIC_Txmit.dataSize + pageAccessAddr) > EEPROM_MAX_SIZE)
				{
				sndsIIC.IIC_Txmit.dataSize = (EEPROM_MAX_SIZE - pageAccessAddr);
				bEndWrite = 1;
				}
			}
		else 
			{
			/* if last page */
			if(page == numPages)
				{
				for(j=0; j < remain_byte; j++)
					{
					sndsIIC.IIC_Txmit.pageBuff[j] = *data++;
					}
				sndsIIC.IIC_Txmit.dataSize = remain_byte;

				/* If (current write pointer address + no. of data) > max offset, 
			  	 *  omit out of range data 
				 */
				if ((sndsIIC.IIC_Txmit.dataSize + pageAccessAddr) > EEPROM_MAX_SIZE)
					{
					sndsIIC.IIC_Txmit.dataSize = (EEPROM_MAX_SIZE - pageAccessAddr);
					bEndWrite = 1;
					}
				}
			/* if still more pages */
			else 
				{
				for(j = 0; j < EEPROM_PAGE_SIZE; j++)
					sndsIIC.IIC_Txmit.pageBuff[j] = *data++;
				sndsIIC.IIC_Txmit.dataSize = EEPROM_PAGE_SIZE;

				/* If (current write pointer address + no. of data) > max offset, 
				 *  omit out of range data 
				 */
				if ((sndsIIC.IIC_Txmit.dataSize + pageAccessAddr) > EEPROM_MAX_SIZE)
					{
					sndsIIC.IIC_Txmit.dataSize = (EEPROM_MAX_SIZE - pageAccessAddr);
					bEndWrite = 1;
					}
				}
			}
		sndsIIC.IIC_Txmit.flag = 0x0;
		sndsIIC.IIC_Txmit.byteWriteCount = 0;
		sndsIIC.IIC_Txmit.writeAddrMSB = (UINT8)((pageAccessAddr>>8) & 0xff);
		sndsIIC.IIC_Txmit.writeAddrLSB = (UINT8)(pageAccessAddr & 0xff);
		do 
			{
			SNDS_IIC_REG_READ (iiccon, status);
			} while (status & (UINT8)SNDS_IIC_ISBUSY); /* Wait until the bus is busy */

		/* Send Start  bit. Enable the interrupt mode and Enable Ack */
		SNDS_IIC_REG_WRITE (iiccon, (SNDS_IIC_START | SNDS_IIC_ACK | SNDS_IIC_IEN));

		/* Send the slave address */
		SNDS_IIC_REG_WRITE (iicbuf, (sndsIIC.IIC_Txmit.slaveAddress & S_WRITE));

		/* Wait till one page of data is written */
		if (semTake (sndsIIC.IIC_Txmit.endofWriteSemID, WAIT_FOREVER) == ERROR)
			{
			nRetVal = ERROR;
			break;
			}

		if (taskDelay(sysClkRateGet() / 50 ) == ERROR) /* to give 5ms delay used for internal write operation */
			{
			nRetVal = ERROR;
			break;
			}
		pageAccessAddr += EEPROM_PAGE_SIZE;
		}

	if (intDisable (sndsIIC.int_vec) == ERROR) /*Enable the IIC bus interrupt */
		return(ERROR);

	/* semGive */
	if (semGive (sndsIIC.IICSemID) == ERROR)
		return(ERROR);

	if (nRetVal == ERROR)
		return(ERROR);
	else
		return (sndsIIC.IIC_Txmit.totalBytesWritten);
	}

/*******************************************************************************
*
* sndsIICWriteISR() - Handle IIC write interrupt
*
* This ISR writes one page of data (32 bytes) into the serial EEPROM.  After the 
* write of a page is complete, the ISR gives the semaphore.
*
* RETURNS: N/A
*/
LOCAL void sndsIICWriteISR()
	{
	if (!(sndsIIC.IIC_Txmit.flag & (UINT8)IIC_TX_BYTE_ADDR_MSB))
		{
		SNDS_IIC_REG_WRITE (iicbuf, sndsIIC.IIC_Txmit.writeAddrMSB);
		sndsIIC.IIC_Txmit.flag |= (UINT8)IIC_TX_BYTE_ADDR_MSB;
		}
	else if (!(sndsIIC.IIC_Txmit.flag & (UINT8)IIC_TX_BYTE_ADDR_LSB))
		{
		SNDS_IIC_REG_WRITE (iicbuf, sndsIIC.IIC_Txmit.writeAddrLSB);		
		sndsIIC.IIC_Txmit.flag |= (UINT8) IIC_TX_BYTE_ADDR_LSB;
		}
	else if ((sndsIIC.IIC_Txmit.byteWriteCount < (UINT32) sndsIIC.IIC_Txmit.dataSize))
		{
		SNDS_IIC_REG_WRITE (iicbuf, sndsIIC.IIC_Txmit.pageBuff[sndsIIC.IIC_Txmit.byteWriteCount++]);
		sndsIIC.IIC_Txmit.totalBytesWritten++;
		}
	else
		{
		SNDS_IIC_REG_WRITE (iiccon, SNDS_IIC_STOP);
		semGive (sndsIIC.IIC_Txmit.endofWriteSemID);
		}
	}

/*******************************************************************************
*
* sndsIICRead - Read data from the IIC device
*
* This routine reads the required amount of data requested by the application from 
* the IIC device.  The data is copied into the buffer provided by the application
*
* RETURNS: OK if read is successful, else ERROR
*/
STATUS sndsIICRead (UINT8 slaveAddr, UINT16 readAddr, UINT8 *pBuff, UINT32 sizeOfData)
	{
	UINT32 status = 0;

	/* Validation of Input Parameters */
	if (slaveAddr != EEPROM_SLAVE_ADDR)	/* Only EEPROM supported on IIC bus on SNDS board */
		return (ERROR);
	if (readAddr >= EEPROM_MAX_SIZE)
		return (ERROR);
	if (pBuff == NULL)		
		return (ERROR);
	
	if ((readAddr + sizeOfData) >= EEPROM_MAX_SIZE)
		sizeOfData = EEPROM_MAX_SIZE - readAddr;

	/* semTake */
	if (semTake (sndsIIC.IICSemID, WAIT_FOREVER) == ERROR)
		return (ERROR);

    /* Connecting the IIC bus interrupt to Read ISR */
	if (intConnect (INUM_TO_IVEC(sndsIIC.int_vec), sndsIICReadISR, NULL) == ERROR)
		return(ERROR);
	if (intEnable (sndsIIC.int_vec) == ERROR)
		return(ERROR);

	/* Initialising the IIC Rx Structure */
	sndsIIC.IIC_Rcv.rxBuff = pBuff;
	sndsIIC.IIC_Rcv.flag = 0x0;
	sndsIIC.IIC_Rcv.byteReadCount = 0x0;
	sndsIIC.IIC_Rcv.dataSize = sizeOfData;
	sndsIIC.IIC_Rcv.slaveAddress = slaveAddr; 
	sndsIIC.IIC_Rcv.readAddrMSB = (UINT8)((readAddr>>8) & 0xff);
	sndsIIC.IIC_Rcv.readAddrLSB = (UINT8)(readAddr & 0xff);
	sndsIIC.IIC_Rcv.totalBytesRead = 0;
	do 
		{
		SNDS_IIC_REG_READ(iiccon, status);
		} while (status & SNDS_IIC_ISBUSY); /* Wait! the iic bus is busy */

	/* Dummy Write */
	SNDS_IIC_REG_WRITE (iiccon, (SNDS_IIC_START|SNDS_IIC_ACK | SNDS_IIC_IEN));
	SNDS_IIC_REG_WRITE (iicbuf, (sndsIIC.IIC_Rcv.slaveAddress & S_WRITE));

	/* Wait till read operation is finished in ISR*/
	semTake (sndsIIC.IIC_Rcv.endofReadSemID, WAIT_FOREVER);

   	if (intDisable (sndsIIC.int_vec) == ERROR) /*Disable the IIC bus interrupt */
		return(ERROR);
	/* semGive */
	if (semGive (sndsIIC.IICSemID) == ERROR)
		return(ERROR);

   	return(sndsIIC.IIC_Rcv.totalBytesRead); 
	}

/*******************************************************************************
*
* sndsIICReadISR() - Handle IIC read interrupt
*
* This ISR reads the required number of bytes from the serial EEPROM.  After the 
* reading is complete, the ISR gives the semaphore.
*
* RETURNS: N/A
*/
LOCAL void sndsIICReadISR()
	{
	/* Dummy write to send read address */
	if (!(sndsIIC.IIC_Rcv.flag & (UINT8)IIC_RX_BYTE_ADDR_MSB))
		{
		/* Send read address MSB */
		SNDS_IIC_REG_WRITE (iicbuf, sndsIIC.IIC_Rcv.readAddrMSB); 
		sndsIIC.IIC_Rcv.flag |= (UINT8)IIC_RX_BYTE_ADDR_MSB; 
		}
	else if (!(sndsIIC.IIC_Rcv.flag & (UINT8) IIC_RX_BYTE_ADDR_LSB))
		{
		/* Send read address LSB */
		SNDS_IIC_REG_WRITE (iicbuf, sndsIIC.IIC_Rcv.readAddrLSB);
		sndsIIC.IIC_Rcv.flag |= (UINT8) IIC_RX_BYTE_ADDR_LSB; 
		}
	else if (!(sndsIIC.IIC_Rcv.flag & (UINT8) IIC_REPEAT_START))
		{
		/* Repeat Start */
		SNDS_IIC_REG_WRITE (iiccon,  SNDS_IIC_REPEAT_START);	

		/* Set IIC master in receiver mode */
		SNDS_IIC_REG_WRITE (iiccon,  (SNDS_IIC_START | SNDS_IIC_ACK | SNDS_IIC_IEN));
		SNDS_IIC_REG_WRITE (iicbuf, sndsIIC.IIC_Rcv.slaveAddress | (UINT8)S_READ);

		sndsIIC.IIC_Rcv.flag |= (UINT8) IIC_REPEAT_START;
		}
	else if (!(sndsIIC.IIC_Rcv.flag & (UINT8)IIC_MULTI_RCV))
		{
		/* This block is used to skip the interrupt raised immediately after writing 
	       slave address. No useful data is available by this time. But this block is used
		   for setting the IIC master in NOACK or ACK mode depending on the condition that
		   only single byte is to be read or Multiple bytes */

		if(sndsIIC.IIC_Rcv.dataSize == 1)
			{
			/* Single byte read. Set to NOACK mode  */
			SNDS_IIC_REG_WRITE(iiccon,  (SNDS_IIC_NOACK | SNDS_IIC_IEN));
			}
		else
			{
			/* Multi bytes read.Send ACK after every byte */
		   	SNDS_IIC_REG_WRITE(iiccon,  (SNDS_IIC_ACK | SNDS_IIC_IEN));
			}
   		sndsIIC.IIC_Rcv.flag |= (UINT8) IIC_MULTI_RCV;
		}

	/* for multibyte read operation.. */
	else if (sndsIIC.IIC_Rcv.byteReadCount < (sndsIIC.IIC_Rcv.dataSize - 1))
		{
		 /* Read a byte to RxBuff till last but one character */
		SNDS_IIC_REG_READ (iicbuf,  sndsIIC.IIC_Rcv.rxBuff[sndsIIC.IIC_Rcv.byteReadCount++]);
		sndsIIC.IIC_Rcv.totalBytesRead++;

		/* if only last byte is remaining, set NOACK mode so that no ACK is sent after 
		 * last byte 
		 */
		if (sndsIIC.IIC_Rcv.byteReadCount == (sndsIIC.IIC_Rcv.dataSize - 1))
			{
			SNDS_IIC_REG_WRITE (iiccon, (SNDS_IIC_NOACK | SNDS_IIC_IEN));
			}
		}
	else
		{  
		/* Receive last data */
		SNDS_IIC_REG_READ (iicbuf, sndsIIC.IIC_Rcv.rxBuff[sndsIIC.IIC_Rcv.byteReadCount]);
		sndsIIC.IIC_Rcv.totalBytesRead++;

        /* STOP IIC Controller */
		SNDS_IIC_REG_WRITE (iiccon, (SNDS_IIC_STOP));
		semGive (sndsIIC.IIC_Rcv.endofReadSemID);
		}
	}

/*******************************************************************************
*
* sndsIICIoctl - Control the IIC device
*
* This routine can be used by the application to set the prescaler value of the 
* IIC device.
*
* RETURNS: OK if successful, else ERROR
*/
STATUS sndsIICIoctl(UINT32 cmd, void* arg)
	{
	UINT32 *pSClk; 
	UINT32 nPreScaleValue;

	pSClk = (UINT32 *) arg;

	if (cmd == SET_PRE_SCALER)
		{
		SNDS_IIC_REG_WRITE(iiccon, SNDS_IIC_RESET_IICCON);
		nPreScaleValue = setPreScaler (*pSClk);
		SNDS_IIC_REG_WRITE (iicps, nPreScaleValue);
		return (OK);
		}
	else
		return (ERROR);
	}

/*******************************************************************************
*
* setPreScaler - Calculate the IIC prescaler value for the given clock speed
*
* RETURNS: Prescaler value
*/
LOCAL UINT32 setPreScaler(UINT32 sclk)
	{
	return ((UINT32) ((((fMCLK_MHz / sclk) - 3) / 16)));
	}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -