📄 ps2socket.c
字号:
/****************************************************************************
*
* MODULE: Wireless PS2 Bus, Host (Socket) Interface
*
* COMPONENT: $RCSfile$
*
* VERSION: $Name$
*
* REVISION: $Revision$
*
* DATED: $Date$
*
* STATUS: $State$
*
* AUTHOR: APV Ward
*
* DESCRIPTION:
* Driver for a PS2 synchronous serial bus, host interface.
* A PS2 host provides a socket service, ie presents a PS2 socket (probably as
* a PC) for a keyboard, mouse or other PS2 device.
* PS2 hosts (socket) act as clock slaves, the PS2 device (plug) controls
* the clock line.
*
* CHANGE HISTORY:
*
* $Log$
*
* LAST MODIFIED BY: $Author$
* $Modtime$
*
****************************************************************************
*
* This software is owned by Jennic and/or its supplier and is protected
* under applicable copyright laws. All rights are reserved. We grant You,
* and any third parties, a license to use this software solely and
* exclusively on Jennic products. You, and any third parties must reproduce
* the copyright and warranty notice and any other legend of ownership on each
* copy or partial copy of the software.
*
* THIS SOFTWARE IS PROVIDED "AS IS". JENNIC MAKES NO WARRANTIES, WHETHER
* EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
* ACCURACY OR LACK OF NEGLIGENCE. JENNIC SHALL NOT, IN ANY CIRCUMSTANCES,
* BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, SPECIAL,
* INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR ANY REASON WHATSOEVER.
*
* Copyright Jennic Ltd 2005, 2006. All rights reserved
*
****************************************************************************/
/****************************************************************************/
/*** Include files ***/
/****************************************************************************/
#include "jendefs.h"
#include "ZKBconfig.h"
#include "PS2socket.h"
#include <AppHardwareApi.h>
/****************************************************************************/
/*** Macro Definitions ***/
/****************************************************************************/
/* Clock and data-line control macros: easier to read */
#define vSET_CLOCK_HIGH vAHI_DioSetDirection( PS2_DIO_CLOCK, (uint32) 0 )
#define vSET_CLOCK_LOW vAHI_DioSetDirection( (uint32) 0, PS2_DIO_CLOCK )
#define vSET_DATA_HIGH vAHI_DioSetDirection( PS2_DIO_DATA, (uint32) 0 )
#define vSET_DATA_LOW vAHI_DioSetDirection( (uint32) 0, PS2_DIO_DATA )
#define boCLOCK_IS_HIGH (u32AHI_DioReadInput() & PS2_DIO_CLOCK)
#define boCLOCK_IS_LOW (!(u32AHI_DioReadInput() & PS2_DIO_CLOCK))
#define boDATA_IS_HIGH (u32AHI_DioReadInput() & PS2_DIO_DATA)
#define boDATA_IS_LOW (!(u32AHI_DioReadInput() & PS2_DIO_DATA))
/* for debug scope */
#define vSET_TRIG_HIGH vAHI_DioSetDirection( DEBUG_EXT_TRIG, (uint32) 0 )
#define vSET_TRIG_LOW vAHI_DioSetDirection( (uint32) 0, DEBUG_EXT_TRIG )
/****************************************************************************/
/*** Type Definitions ***/
/****************************************************************************/
/****************************************************************************/
/*** Local Function Prototypes ***/
/****************************************************************************/
PRIVATE uint16 u16FindStartBit (void);
PRIVATE uint16 u16FindDataBit (void);
PRIVATE uint16 u16FindParityBit (uint8);
PRIVATE uint16 u16FindStopBit (void);
PRIVATE uint16 u16FindClkFallingEdge (void);
PRIVATE void vIdle (void);
/* Write byte functions */
PRIVATE void vWait100us (void);
PRIVATE bool_t boDeviceClkStarted (void);
PRIVATE bool_t boDataBitClocked (void);
PRIVATE bool_t boDataWentLow (void);
PRIVATE bool_t boClockWentLow (void);
PRIVATE bool_t boDataClkReleased (void);
/****************************************************************************/
/*** Exported Functions ***/
/****************************************************************************/
/****************************************************************************
*
* NAME: u16PS2socketInit
*
* DESCRIPTION:
* Initialises the PS2 interface into a known state by setting the PS2
* bus to idle.
*
* PARAMETERS: None.
*
* RETURNS: PS2_STATUS_SUCCESS
*
****************************************************************************/
PUBLIC uint16 u16PS2socketInit(void)
{
/* set debug scope trigger as output and low */
vAHI_DioSetOutput ( (uint32) 0, DEBUG_EXT_TRIG);
vAHI_DioSetDirection( (uint32) 0, DEBUG_EXT_TRIG);
vIdle();
return PS2_STATUS_SUCCESS;
}
/****************************************************************************
*
* NAME: u16PS2socketClose
*
* DESCRIPTION:
* Closes the PS2 interface by setting the PS2 bus to idle.
*
* PARAMETERS: None.
*
* RETURNS: PS2_STATUS_SUCCESS
*
****************************************************************************/
PUBLIC void vPS2socketClose(void)
{
vIdle();
}
/****************************************************************************
*
* NAME: vPS2socketBusEnable
*
* DESCRIPTION:
* Enable the PS2 bus by releasing the open-collector clock line.
*
* PARAMETERS: None.
*
* RETURNS: Void
*
****************************************************************************/
PUBLIC void vPS2socketBusEnable(void)
{
/* set clock and data lines as inputs */
vAHI_DioSetDirection( (PS2_DIO_DATA | PS2_DIO_CLOCK), (uint32) 0);
}
/****************************************************************************
*
* NAME: vPS2socketBusDisable
*
* DESCRIPTION:
* Disable the PS2 bus by pulling the open-collector clock line low.
*
* PARAMETERS: None.
*
* RETURNS: Void
*
****************************************************************************/
PUBLIC void vPS2socketBusDisable(void)
{
vIdle();
}
/****************************************************************************
*
* NAME: boPS2socketBusReady
*
* DESCRIPTION:
* Returns the current status of the PS2 clock and data lines.
* Both lines low indicates bus active - also consistant with a start bit.
*
* PARAMETERS: None.
*
* RETURNS: TRUE if both clock and data lines are low.
*
****************************************************************************/
PUBLIC bool_t boPS2socketBusReady(void)
{
/* if clock and data lines are low, then PS2 data burst has started */
return (bool_t) ( ! (u32AHI_DioReadInput() & (PS2_DIO_CLOCK | PS2_DIO_DATA)) );
}
/****************************************************************************
*
* NAME: boPS2socketBusIdle
*
* DESCRIPTION:
* Returns the current status of the PS2 clock and data lines.
* Both lines high indicates bus idle.
*
* PARAMETERS: None.
*
* RETURNS: TRUE if both clock and data lines are high (bus idle).
*
****************************************************************************/
PUBLIC bool_t boPS2socketBusIdle(void)
{
/* if BOTH clock and data lines are high, then PS2 bus is idle */
return (bool_t)( (u32AHI_DioReadInput() & (PS2_DIO_CLOCK | PS2_DIO_DATA))
== (PS2_DIO_CLOCK | PS2_DIO_DATA)
);
}
/****************************************************************************
*
* NAME: boPS2socketBusStarted
*
* DESCRIPTION:
* Returns the current status of the PS2 clock and data lines.
* Both lines low indicates bus active - also consistant with a start bit.
* Scan lines for at least 100 us, return earlier if bus cycle starts.
*
* PARAMETERS: None.
*
* RETURNS: TRUE if both clock and data lines are low.
*
****************************************************************************/
PUBLIC bool_t boPS2socketBusStarted(void)
{
vAHI_TimerStartSingleShot(PS2_SOCKET_TIMER, 100, 100);
while ( ! (u8AHI_TimerFired(PS2_SOCKET_TIMER) & E_AHI_TIMER_INT_PERIOD) )
{
/* if clock and data lines are low, then PS2 data burst has started */
if( ! (u32AHI_DioReadInput() & (PS2_DIO_CLOCK | PS2_DIO_DATA)) )
{
vAHI_TimerStop(PS2_SOCKET_TIMER);
return TRUE;
}
}
return FALSE;
}
/****************************************************************************
*
* NAME: u16PS2socketRead
*
* DESCRIPTION:
* Read a data burst from the PS2 device.
*
* A full description of the PS2 bus protocol would not be appropriate here.
* However, here is a simple overview of a read sequence:
*
* PS2 device drives the clock line, unless the line is already low(device disabled).
* Falling edge of the clock line strobes valid date.
* Data burst starts with an active low start bit,
* followed by 8 data bits (LSB first)
* followed by an ODD parity bit,
* followed by an active high stop bit.
*
* At any stage a data burst can be aborted if the open-collector clock line
* is pulled low for more than 100 uS. If this happens before the 11th data bit
* period has completed, then the data burst must be sent again (after the clock
* line has been released for more than 50 uS).
*
* PARAMETERS: None.
*
* RETURNS: uint16 - PS2 code (8 bits), or error code (16 bits)
*
****************************************************************************/
PUBLIC uint16 u16PS2socketRead(void)
{
uint8 u8Loop;
uint8 u8Read = 0;
uint8 u8ParityCount = 0;
uint16 u16Status;
/* enter here after detecting a falling edge on PS2 clock line */
/* check for the start bit from the bus, abort if not present */
u16Status = u16FindStartBit();
if (u16Status != PS2_STATUS_START_BIT)
return u16Status;
/* Now look for the data part of the transmission */
for (u8Loop = 0; u8Loop < PS2_DATA_WIDTH; u8Loop++)
{
u16Status = u16FindDataBit(); /* look for single clocked-data bit */
switch (u16Status)
{
case PS2_STATUS_DATA_1_BIT:
u8Read |= (0x01 << u8Loop);
u8ParityCount++;
break;
case PS2_STATUS_DATA_0_BIT:
/* bit accumulator already 0 */
break;
case PS2_ERROR_DEVICE_CLK_DATA_TO: /* timeout */
return u16Status;
break;
default:
/* Error condition catch-all */
return PS2_ERROR_UNKNOWN;
break;
}
}
/* look for the parity bit, abort if not correct */
u16Status = u16FindParityBit(u8ParityCount);
if (u16Status != PS2_STATUS_SUCCESS)
return u16Status;
/* look for a stop bit, abort if not present */
u16Status = u16FindStopBit();
if (u16Status == PS2_ERROR_NO_STOP_BIT)
return u16Status;
/* successful read from the PS2 bus, return code */
return (uint16) u8Read;
}
/****************************************************************************
*
* NAME: u16PS2socketWrite
*
* DESCRIPTION:
* PS2 bus write sequence. The PS2 bus is bidirectional, for a PS2 keyboard
* the back channel carries config and status commands (auto-repeat rates,
* shift, caps lock LED control etc).
*
* This function NOT used in this keyboard demo application.
*
* PARAMETERS: None.
*
* RETURNS: PS2_STATUS_SUCCESS if write successful, else these error coes
* PS2_ERROR_DEVICE_CLK_START_TO
* PS2_ERROR_DEVICE_CLK_DATA_TO
* PS2_ERROR_DEVICE_CLK_PARY_TO
* PS2_ERROR_DEVICE_ACK_0
* PS2_ERROR_DEVICE_ACK_1
* PS2_ERROR_DEVICE_ACK_2
*
****************************************************************************/
PUBLIC uint16 u16PS2socketWrite(uint8 u8Datum)
{
uint8 u8Loop;
uint8 u8ParityCount = 0;
/* DEBUG */
vSET_TRIG_HIGH;
// 1) Bring the Clock line low for at least 100 microseconds.
// Both output latches low, but data is an input
vIdle();
vWait100us();
// 2) Bring the Data line low.
vSET_DATA_LOW;
// 3) Release the Clock line.
vSET_CLOCK_HIGH;
// 4) Wait for the device to bring the Clock line low.
if ( ! boDeviceClkStarted() )
return PS2_ERROR_DEVICE_CLK_START_TO;
/* send data byte */
for (u8Loop = 0; u8Loop < PS2_DATA_WIDTH; u8Loop++)
{
// 5) Set/reset the Data line to send the first data bit
if (u8Datum & (0x01 << u8Loop))
{
vSET_DATA_HIGH;
u8ParityCount++;
}
else
{
vSET_DATA_LOW;
}
// 6) Wait for the device to bring Clock high.
// 7) Wait for the device to bring Clock low.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -