School of Electrical Engineering and Computer Science
PNSS-1 DATA HANDLING UNIT
INTERNSHIP REPORT
By: Ali Athar, Mutee ur Rehman, Armghan Ahmad Khan,
Muneeb Zafar, Abdul Moiz, Mujtaba Hasan
Data Handling Unit (DHU):
General Description:
The satellite will have different modules performing different functionality independently. Data
Handling Unit is the part that will control and co-ordinate the information transmitted among these
different modules.
The Data Handling module will control the flow of information between ground station and all the
modules of the satellite through the communication module. The data handling module is the gateway
for incoming and outgoing telemetry and telecommand traffic. It will receive, validate, decode, and
distribute commands from the ground, payload, or a subsystem to other subsystems.
Functionalities:
Following will be the set of functionality that the DHU will perform in the satellite.
 The DHU will acquire and process the telemetry of all the subsystems.
 The DHU will generate and process/transmit the on-board Tele-command to the relevant
subsystems.
 The DHU will perform the Autonomous Bus Management of the satellite.
 The DHU of satellite will be capable of acquiring and processing the Direct Telemetry.
 The DHU of satellite will be capable of generating and executing the Direct Tele-commands.
 The DHU will perform the overall health monitoring of the satellite.
 The DHU will provide the capability to transfer data between satellite subsystems, as needed
using suitable communication protocols.
 The DHU will allow simultaneous data collection and data downlink.
 DHU will act as a master CAN controller.
 The DHU will perform the CCSDS packetization for Telemetry and depacketization of Tele-
command data, respectively.
 Where there is no automatic recovery, sufficient telemetry data will be provided to enable
ground detection of failures in a timely manner.
 The DHU will be capable of decoding and validating the Tele-commands into time-tagged or
event triggered sequences of derived commands throughout the mission life.
 The DHU will distribute timing and synchronization signals to subsystems.
 The DHU will generate onboard timing reference through on board GPS.
 The DHU will have physical redundancy i.e. Nominal DHU and Redundant DHU in single housing
and will operate in cold redundancy.
 The DHU will be equipped with self-testing and diagnostic capability of all units and will
interface upon start up or by ground command. Status of self-testing and diagnostic will be
provided via telemetry to ground.
 Cross-strapping will be provided so that redundant units can be switched in and out of the data
bus and failed units can be isolated without affecting the subsystem performance and
functionality.
 Failure of any one unit will not disable any data transfer operation or any interface from
operation.
Functional Diagram of DHU as part of overall satellite design:
Universal Asynchronous Receiver
Transmitter (UART)
Introduction:
 One of the oldest and most basic forms of serial communication.
 Individual bits of a byte are taken and transmitted in a sequential fashion.
 Supports full duplex communication
 Each byte is preceded by a start bit and the byte is terminated using a stop bit at the end.
 Minimal hardware is required.
Salient Features of UART On PSoC:
 Communication is made easier by user-friendly libraries and helper functions.
 Baud rates of up to 115.2 kbits/s are supported.
 Additional data integrity checks which are traditionally not used with UART are provided by the
PSoC IDE such as 2 out of 3 voting per bit.
 Numerous interrupts are provided by the platform which help in more efficient manipulation of
data and enhance multi-tasking capabilities.
 4-byte FIFO buffer is inserted between receiver/transmitter register and UART bus which gives
the controller more time to service interrupts and reduces the likelihood of data loss.
Hardware Connection:
UART communication can be setup between two PSoC kits using a simple null modem connection. Since
both devices operate on TTL standards, no MAX232 voltage converter circuit is required.
Software Aspect of Communication:
 UART is a very basic communication technique that simply sequentially transmits bits down a
single line.
 In order to be able to reliably communicate between two devices using UART, the user must
define additional protocol features.
 Our goal was to enable devices to transmit variable number of bytes using UART and a versatile,
generic receiver that could handle such transmissions.
On a specific note, the first major problem encountered was that unlike other protocols such as I2
C
and CAN, UART transmission on its own provides no feedback as to whether the transmitted data
was actually received or not. Therefore if we simply start to transmit an array via UART, there is no
way to verify whether it was received or whether the receiving device was even powered on. To
resolve this problem, we had to define a handshaking procedure of our own so that prior to any
data being communicated, both transmitter and receiver can be sure that the other device is
powered on and functional. The following flow diagram outlines the handshake procedure:
Once the handshake is complete, it is verified that both transmitter and receiver are ready to
receive data. However, to achieve the versatility required, it was necessary to confine any data that
had to be transmitted into packets and an algorithm that could de-packetize the data at the
receiving end. It therefore became necessary to define a custom protocol that suited the
requirements. The following protocol was hence implemented:
To illustrate an example, suppose we are required to transmit an array consisting of 10 data
elements of type character. Following is a breakdown of the packet that would be formed to
transmit this data:
1st
Byte: 0x01 (assuming character has been assigned an ID of 1)
2nd
Byte: 0x0A (since 10 data elements have to be transmitted)
3rd
Byte – 13th
Byte: Data block of 10 bytes
14th
Byte: Trailing byte consisting of Cyclic Redundancy Check code.
Cyclic Redundancy Check (CRC):
In order to verify the integrity of the data that was transmitted, a CRC code is generated using the
entire data block that needs to be transmitted. This 8-bit (single byte) code is obtained by
performing binary division on the array elements using a constant polynomial factor and the
remainder left at the end is the required code. This code is transmitted along with the packet as
shown above. At the receiving end, the device again generates a CRC code from the data it received
and cross-checks this code against that which it has received. If the two match, then it is verified
that the data was received correctly.
Firmware Code:
Below is the actual embedded C code which was used to achieve UART communication between
two PSoC kits. The entire procedure is interrupt driven therefore it doesn’t keep the controller
occupied. The initiation of communication can be done by calling a user-made function. The
transmitter and receiver execute different codes, however these can very easily be combined to
achieve bidirectional communication.
RECEIVER:
#define HANDSHAKE_VAR 255
#define ERROR_VAR 254
#define POLY 0xB2
uint8 isHSDone = 0;
uint8 handshake_byte = 0;
uint8 i=0;
uint8 _formatSpecifier = 0;
uint8 _packetSize = 0;
uint8 _blockData = 0;
uint8 _blcokDataCount = 0;
uint8 _CRCbyte = 0;
uint8 dataBlock[10];
uint8 CRC;
code unsigned char crc8_table[] = {
0x00, 0x3e, 0x7c, 0x42, 0xf8, 0xc6, 0x84, 0xba, 0x95, 0xab, 0xe9,
0xd7,
0x6d, 0x53, 0x11, 0x2f, 0x4f, 0x71, 0x33, 0x0d, 0xb7, 0x89, 0xcb,
0xf5,
0xda, 0xe4, 0xa6, 0x98, 0x22, 0x1c, 0x5e, 0x60, 0x9e, 0xa0, 0xe2,
0xdc,
0x66, 0x58, 0x1a, 0x24, 0x0b, 0x35, 0x77, 0x49, 0xf3, 0xcd, 0x8f,
0xb1,
0xd1, 0xef, 0xad, 0x93, 0x29, 0x17, 0x55, 0x6b, 0x44, 0x7a, 0x38,
0x06,
0xbc, 0x82, 0xc0, 0xfe, 0x59, 0x67, 0x25, 0x1b, 0xa1, 0x9f, 0xdd,
0xe3,
0xcc, 0xf2, 0xb0, 0x8e, 0x34, 0x0a, 0x48, 0x76, 0x16, 0x28, 0x6a,
0x54,
0xee, 0xd0, 0x92, 0xac, 0x83, 0xbd, 0xff, 0xc1, 0x7b, 0x45, 0x07,
0x39,
0xc7, 0xf9, 0xbb, 0x85, 0x3f, 0x01, 0x43, 0x7d, 0x52, 0x6c, 0x2e,
0x10,
0xaa, 0x94, 0xd6, 0xe8, 0x88, 0xb6, 0xf4, 0xca, 0x70, 0x4e, 0x0c,
0x32,
0x1d, 0x23, 0x61, 0x5f, 0xe5, 0xdb, 0x99, 0xa7, 0xb2, 0x8c, 0xce,
0xf0,
0x4a, 0x74, 0x36, 0x08, 0x27, 0x19, 0x5b, 0x65, 0xdf, 0xe1, 0xa3,
0x9d,
0xfd, 0xc3, 0x81, 0xbf, 0x05, 0x3b, 0x79, 0x47, 0x68, 0x56, 0x14,
0x2a,
0x90, 0xae, 0xec, 0xd2, 0x2c, 0x12, 0x50, 0x6e, 0xd4, 0xea, 0xa8,
0x96,
0xb9, 0x87, 0xc5, 0xfb, 0x41, 0x7f, 0x3d, 0x03, 0x63, 0x5d, 0x1f,
0x21,
0x9b, 0xa5, 0xe7, 0xd9, 0xf6, 0xc8, 0x8a, 0xb4, 0x0e, 0x30, 0x72,
0x4c,
0xeb, 0xd5, 0x97, 0xa9, 0x13, 0x2d, 0x6f, 0x51, 0x7e, 0x40, 0x02,
0x3c,
0x86, 0xb8, 0xfa, 0xc4, 0xa4, 0x9a, 0xd8, 0xe6, 0x5c, 0x62, 0x20,
0x1e,
0x31, 0x0f, 0x4d, 0x73, 0xc9, 0xf7, 0xb5, 0x8b, 0x75, 0x4b, 0x09,
0x37,
0x8d, 0xb3, 0xf1, 0xcf, 0xe0, 0xde, 0x9c, 0xa2, 0x18, 0x26, 0x64,
0x5a,
0x3a, 0x04, 0x46, 0x78, 0xc2, 0xfc, 0xbe, 0x80, 0xaf, 0x91, 0xd3,
0xed,
0x57, 0x69, 0x2b, 0x15 };
void initHandshake()
{
CYGlobalIntEnable;
RX_ISR_Start();
RX_ISR_SetVector(RX_ISR_Interrupt);
while (!isHSDone);
LCD_Char_1_Position(0u, 5u);
LCD_Char_1_PrintString("Sync Done");
}
unsigned crc8_slow(unsigned crc, unsigned char *dataBlock, uint8 len)
{
unsigned char *end;
if (len == 0)
return crc;
crc ^= 0xff;
end = dataBlock + len;
do {
crc ^= *dataBlock++;
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
} while (dataBlock < end);
return crc ^ 0xff;
}
void printArray()
{
LCD_Char_1_Position(0u, 0u);
LCD_Char_1_PrintHexUint8(dataBlock[0]);
LCD_Char_1_PrintHexUint8(dataBlock[1]);
LCD_Char_1_PrintHexUint8(CRC);
LCD_Char_1_Position(1u, 0u);
for(i=2; i<dataBlock[1]+2; i++)
{
LCD_Char_1_PrintHexUint8(dataBlock[i]);
}
}
int main()
{
UART_1_Start();
LCD_Char_1_Start();
initHandshake();
for(;;)
{
}
}
void TX_ISR()
{
return;
}
void RX_ISR()
{
if (!isHSDone)
{
handshake_byte = UART_1_GetByte();
if (handshake_byte == HANDSHAKE_VAR)
UART_1_PutChar(HANDSHAKE_VAR);
isHSDone = 1;
_formatSpecifier = 1;
return;
}
else if (_formatSpecifier)
{
dataBlock[i] = UART_1_GetChar();
i++;
_formatSpecifier = 0;
_packetSize = 1;
}
else if (_packetSize)
{
dataBlock[i] = UART_1_GetChar();
i++;
_packetSize = 0;
_blockData = 1;
}
else if (_blockData)
{
dataBlock[i] = UART_1_GetChar();
i++;
if (i == dataBlock[1]+2)
{
_blockData = 0;
_CRCbyte = 1;
}
}
else if (_CRCbyte)
{
CRC = UART_1_GetChar();
if (CRC == crc8_slow(0, dataBlock, dataBlock[1]+2))
{
CYGlobalIntDisable;
printArray();
}
else
{
CYGlobalIntDisable;
LCD_Char_1_Position(1u, 8u);
LCD_Char_1_PrintString("CRC err");
printArray();
}
_CRCbyte = 0;
}
}
TRANSMITTER:
#define HANDSHAKE_VAR 255
#define POLY 0xB2
#define packetSize 5
uint8 isHSDone = 0;
uint8 handshake_byte = 0;
uint8 TxByte=0;
code unsigned char crc8_table[] = {
0x00, 0x3e, 0x7c, 0x42, 0xf8, 0xc6, 0x84, 0xba, 0x95, 0xab, 0xe9,
0xd7,
0x6d, 0x53, 0x11, 0x2f, 0x4f, 0x71, 0x33, 0x0d, 0xb7, 0x89, 0xcb,
0xf5,
0xda, 0xe4, 0xa6, 0x98, 0x22, 0x1c, 0x5e, 0x60, 0x9e, 0xa0, 0xe2,
0xdc,
0x66, 0x58, 0x1a, 0x24, 0x0b, 0x35, 0x77, 0x49, 0xf3, 0xcd, 0x8f,
0xb1,
0xd1, 0xef, 0xad, 0x93, 0x29, 0x17, 0x55, 0x6b, 0x44, 0x7a, 0x38,
0x06,
0xbc, 0x82, 0xc0, 0xfe, 0x59, 0x67, 0x25, 0x1b, 0xa1, 0x9f, 0xdd,
0xe3,
0xcc, 0xf2, 0xb0, 0x8e, 0x34, 0x0a, 0x48, 0x76, 0x16, 0x28, 0x6a,
0x54,
0xee, 0xd0, 0x92, 0xac, 0x83, 0xbd, 0xff, 0xc1, 0x7b, 0x45, 0x07,
0x39,
0xc7, 0xf9, 0xbb, 0x85, 0x3f, 0x01, 0x43, 0x7d, 0x52, 0x6c, 0x2e,
0x10,
0xaa, 0x94, 0xd6, 0xe8, 0x88, 0xb6, 0xf4, 0xca, 0x70, 0x4e, 0x0c,
0x32,
0x1d, 0x23, 0x61, 0x5f, 0xe5, 0xdb, 0x99, 0xa7, 0xb2, 0x8c, 0xce,
0xf0,
0x4a, 0x74, 0x36, 0x08, 0x27, 0x19, 0x5b, 0x65, 0xdf, 0xe1, 0xa3,
0x9d,
0xfd, 0xc3, 0x81, 0xbf, 0x05, 0x3b, 0x79, 0x47, 0x68, 0x56, 0x14,
0x2a,
0x90, 0xae, 0xec, 0xd2, 0x2c, 0x12, 0x50, 0x6e, 0xd4, 0xea, 0xa8,
0x96,
0xb9, 0x87, 0xc5, 0xfb, 0x41, 0x7f, 0x3d, 0x03, 0x63, 0x5d, 0x1f,
0x21,
0x9b, 0xa5, 0xe7, 0xd9, 0xf6, 0xc8, 0x8a, 0xb4, 0x0e, 0x30, 0x72,
0x4c,
0xeb, 0xd5, 0x97, 0xa9, 0x13, 0x2d, 0x6f, 0x51, 0x7e, 0x40, 0x02,
0x3c,
0x86, 0xb8, 0xfa, 0xc4, 0xa4, 0x9a, 0xd8, 0xe6, 0x5c, 0x62, 0x20,
0x1e,
0x31, 0x0f, 0x4d, 0x73, 0xc9, 0xf7, 0xb5, 0x8b, 0x75, 0x4b, 0x09,
0x37,
0x8d, 0xb3, 0xf1, 0xcf, 0xe0, 0xde, 0x9c, 0xa2, 0x18, 0x26, 0x64,
0x5a,
0x3a, 0x04, 0x46, 0x78, 0xc2, 0xfc, 0xbe, 0x80, 0xaf, 0x91, 0xd3,
0xed,
0x57, 0x69, 0x2b, 0x15 };
uint8 array[7] = {1, 5, 10, 11, 12, 13, 14};
void initHandshake()
{
UART_1_PutChar(HANDSHAKE_VAR);
RX_ISR_Start();
RX_ISR_SetVector(RX_ISR_Interrupt);
while (handshake_byte != HANDSHAKE_VAR);
isHSDone = 1;
LCD_Char_1_Position(0u, 5u);
LCD_Char_1_PrintString("Sync Done");
}
unsigned crc8_slow(unsigned crc, unsigned char *dataBlock, uint8 len)
{
unsigned char *end;
if (len == 0)
return crc;
crc ^= 0xff;
end = dataBlock + len;
do {
crc ^= *dataBlock++;
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
} while (dataBlock < end);
return crc ^ 0xff;
}
int main()
{
uint8 i;
uint8 CRC;
CYGlobalIntDisable;
UART_1_Start();
LCD_Char_1_Start();
TX_ISR_Start();
TX_ISR_SetVector(TX_ISR_Interrupt);
CYGlobalIntEnable;
initHandshake();
CRC = crc8_slow(0, array, packetSize+2);
for(i=0; i<7; i++)
{
UART_1_PutChar(array[i]);
}
UART_1_PutChar(CRC);
while(1)
{
LCD_Char_1_Position(0u, 0u);
LCD_Char_1_PrintString("HS/Array Out");
LCD_Char_1_Position(1u, 0u);
LCD_Char_1_PrintHexUint8(CRC);
}
}
void TX_ISR()
{
return;
}
void RX_ISR()
{
if (!isHSDone)
{
handshake_byte = UART_1_GetByte();
}
else
{
}
}
Inter-Integrated Circuit (I2C)
General Description:
I2
C (also known as IIC) stands for Inter-Integrated Circuit. It is a multi-master multi-slave serial computer
bus. The I2C bus is an industry-standard, two-wire hardware interface developed by Philips. It is used to
connect low speed peripherals with mother-board. I²C uses only two bidirectional open-drain lines,
Serial Data Line (SDA) and Serial Clock Line (SCL) which are pulled up with resistors. The typical voltage
levels are +5 V or +3.3 V. The master initiates all communication on the I2C bus and supplies the clock
for all slave devices. Each I2C bus has a 7-bit address space for different slaves. Each device connected
to the bus has got a unique address. Data divided into 8-bit bytes. A few control bits for controlling the
communication start, end, direction and for an acknowledgment mechanism.. The I2C component
supports standard clock speeds up to 1000 kbps.
Hardware Connections:
Nodes :
There are two types of nodes in I2C bus
 Master node —that generates the clock signal and starts communication with slaves
 Slave node — node that receives the clock and responds when addressed by the master
Modes of operation:
There are four possible modes of operation for a given bus device:
 master transmit — master is sending data to a slave
 master receive — master is receiving data from a slave
 slave transmit — slave is sending data to the master
 slave receive — slave is receiving data from the master
I2C and PSOC development kit:
 Supports Slave, Master, Multi-Master and Multi-Master-Slave operation
 Only two pins (SDA and SCL) required to interface to I2C bus
 Standard data rates of 100/400/1000 kbps supported
 High level APIs require minimal user programming
Data Rate
This parameter is used to set the I2C data rate value up to 1000 kbps; the actual speed may differ based
on available clock speed and divider range. The standard speeds are 50, 100 (default), 400, and 1000
kbps.
Pin Connections:
SCL P12[0]
SDA P12[1]
Master Operation:
When the master wants to transmit something it starts it by sending a start bit followed by the 7-bit
address of the slave it wishes to communicate with, which is finally followed by a single bit representing
whether it wishes to write(0) to or read(1) from the slave.
Slave Operation:
If the slave exists on the bus then it will respond with an acknowledgement signal an ACK bit. The
master then continues in either transmit or receive mode (according to the read/write bit it sent), and
the slave continues in its complementary mode (receive or transmit, respectively).
The address and the data bytes are sent Big Endian Format. The start bit is indicated by a high-to-low
transition of SDA with SCL high; the stop bit is indicated by a low-to-high transition of SDA with SCL high.
All other transitions of SDA take place with SCL low.
If the master wishes to write to the slave then it repeatedly sends a byte with the slave sending an ACK
bit. (In this situation, the master is in master transmit mode and the slave is in slave receive mode.)
If the master wishes to read from the slave then it repeatedly receives a byte from the slave, the master
sending an ACK bit after every byte but the last one. (In this situation, the master is in master receive
mode and the slave is in slave transmit mode.)
The master then either ends transmission with a stop bit. Timing Diagram:
Over the course of the internship, we managed to come up with three different methods of
communication using I2C protocol. Each of these is elaborated below with the full code pasted:
2D Array Transmission
Image data is represented in a 2D array form. Typically, the images taken by the payload of the satellite
have a resolution of 796x800. Such a large image cannot be transmitted all at once and hence the
general approach is to break such a large array into numerous, smaller arrays and transmit these
segments one-by-one. As an example, the code below transmits a 4x4 matrix which is first broken into
four 2x2 matrices before transmission. The size of the 2D array and that of the smaller segment can
easily be changed by altering the iterator limits in the nested loops in the code below:
TRANSMITTER (Master):
#include <project.h>
#include <stdio.h>
#define I2C_SLAVE_ADDRESS (0x10u)
#define I2C_SLAVE_ADDRESS1 (0x08u)
#define WR_BUFFER_SIZE1 (0x05u)
#define WR_BUFFER_SIZE2 (0x05u)
void initI2C()
{
LCD_Char_Start();
LCD_Char_PrintString("Master Start");
CyDelay(1000u);
LCD_Char_ClearDisplay();
CyGlobalIntEnable;
I2CM_Start();
}
uint8 Master_send_I2CData(uint8* buffer, uint8 buffer_size, uint8
slave_address)
{
uint8 i = 0u;
uint8 temp;
uint8 res;
/* Attempt to initiate communication with the slave until the function
* completes without error.
*/
do
{
/* The syntax below automatically writes a buffer of data to a
slave
* device from start to stop.
*/
temp = I2CM_MasterWriteBuf(slave_address, buffer, buffer_size,
I2CM_MODE_COMPLETE_XFER);
}
while (temp != I2CM_MSTR_NO_ERROR);
/* Wait for the data transfer to complete */
while(I2CM_MasterStatus() & I2CM_MSTAT_XFER_INP);
temp = I2CM_MasterClearStatus();
/* If there is an error while transferring data */
if(temp & I2CM_MSTAT_ERR_XFER)
{
/* Indicate the error */
LCD_Char_PrintString("I2C Error! ");
CyDelay(2000u/*ms*/);
LCD_Char_ClearDisplay();
res = 0;
}
else /* Write completed without error */
{
res = 1;
}
LCD_Char_ClearDisplay();
return res;
}
int main()
{
uint8 x, i, j, k, a;
uint8 array[4][4] = { 1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16 };
uint8 segment[4];
initI2C();
for(;;)
{
int z = 0;
for (x = 0; x < 3; x += 2)
{
for (i = 0; i < 3; i += 2)
{
for (j = 0; j < 2; j++)
{
for (k = 0; k < 2; k++)
{
segment[z] = array[x + j][i + k];
z++;
}
}
Master_send_I2CData(segment, 4, 8);
a=0;
for(j=0;j<2;j++)
{
for (k=0; k<2; k++)
{
LCD_Char_Position(j, k*3);
LCD_Char_PrintHexUint8(segment[a]);
a++;
}
}
z = 0;
CyDelay(2000);
}
}
}
}
/* [] END OF FILE */
RECEIVER (Slave):
#include <project.h>
#define WR_BUFFER_SIZE (4u)
void recI2CData(uint8* buffer, uint8 buffer_size)
{
uint8 i = 0u;
uint8 j, k;
uint8 byteCount = 0u;
/* Check if the slave buffer has been read */
if(I2CS_SlaveStatus() & I2CS_SSTAT_WR_CMPLT)
{
byteCount = I2CS_SlaveGetWriteBufSize();
I2CS_SlaveClearWriteStatus();
I2CS_SlaveClearWriteBuf();
/* If both bytes of the read buffer have been read */
if(byteCount == buffer_size)
{
/* Display data that was placed in the buffer on the
LCD and verify
* this is same as that received by master */
LCD_Char_Position(0,0);
for(j=0;j<2;j++)
{
for (k=0; k<2; k++)
{
LCD_Char_Position(j, k*3);
LCD_Char_PrintHexUint8(buffer[i]);
i++;
}
}
}
else /* Wrong number of bytes read */
{
LCD_Char_ClearDisplay();
LCD_Char_PrintString("I2C Slave Error!");
}
}
}
int main()
{
uint8 i = 0u;
uint8 byteCount = 0u;
uint8 sample_segment[WR_BUFFER_SIZE];
/* Set up slave read data buffer */
I2CS_SlaveInitWriteBuf((uint8 *) sample_segment, WR_BUFFER_SIZE);
LCD_Char_Start();
LCD_Char_PrintString("Slave Start");
CyDelay(1000u/*ms*/);
LCD_Char_ClearDisplay();
CyGlobalIntEnable;
I2CS_Start();
for(;;)
{
recI2CData(sample_segment,WR_BUFFER_SIZE);
}
} /* End of main */
/* [] END OF FILE */
Bidirectional Data Transfer:
Master receives an array from one slave device with a specified address and then sends the data
to another slave having another slave address (indirect slave-to-slave communication).
MASTER (Transmitter/Receiver):
#include <project.h>
#include <stdio.h>
#define I2C_SLAVE_ADDRESS (0x10u)
#define I2C_SLAVE_ADDRESS1 (0x08u)
#define WR_BUFFER_SIZE1 (0x05u)
#define WR_BUFFER_SIZE2 (0x05u)
void initI2C()
{
LCD_Char_Start();
LCD_Char_PrintString("Master Start");
CyDelay(1000u);
LCD_Char_ClearDisplay();
CyGlobalIntEnable;
I2CM_Start();
}
uint8 Master_recieve_I2CData(uint8 slave_address, uint8* buffer, uint8
buffer_size)
{
uint8 i = 0u;
uint8 temp;
uint8 res;
do
{
/* The syntax below automatically writes a buffer of data to a
slave
* device from start to stop.
*/
temp = I2CM_MasterReadBuf(slave_address, buffer, buffer_size,
I2CM_MODE_COMPLETE_XFER);
}
while (temp != I2CM_MSTR_NO_ERROR);
/* Wait for the data transfer to complete */
while(I2CM_MasterStatus() & I2CM_MSTAT_XFER_INP);
temp = I2CM_MasterClearStatus();
/* If there is an error while transferring data */
if (temp & I2CM_MSTAT_ERR_XFER)
{
/* Indicate the error */
res = 0;
LCD_Char_PrintString("I2C Error! ");
CyDelay(2000u/*ms*/);
LCD_Char_ClearDisplay();
}
else /* Write completed without error */
{
/* For verification purposes, display the Reading on the LCD */
res = 1;
LCD_Char_Position(0,0);
for(i=0;i<buffer_size;i++)
{
LCD_Char_PutChar(buffer[i]);
//CyDelay(100u/*ms*/);
}
}
/* Delay introduced for ease of reading LCD */
CyDelay(1000u/*ms*/);
LCD_Char_ClearDisplay();
CyDelay(1000u/*ms*/);
return res;
}
uint8 Master_send_I2CData(uint8* buffer, uint8 buffer_size, uint8
slave_address)
{
uint8 i = 0u;
uint8 temp;
uint8 res;
/* Attempt to initiate communication with the slave until the function
* completes without error.
*/
do
{
/* The syntax below automatically writes a buffer of data to a
slave
* device from start to stop.
*/
temp = I2CM_MasterWriteBuf(slave_address, buffer, buffer_size,
I2CM_MODE_COMPLETE_XFER);
}
while (temp != I2CM_MSTR_NO_ERROR);
/* Wait for the data transfer to complete */
while(I2CM_MasterStatus() & I2CM_MSTAT_XFER_INP);
temp = I2CM_MasterClearStatus();
/* If there is an error while transferring data */
if(temp & I2CM_MSTAT_ERR_XFER)
{
/* Indicate the error */
LCD_Char_PrintString("I2C Error! ");
CyDelay(2000u/*ms*/);
LCD_Char_ClearDisplay();
res = 0;
}
else /* Write completed without error */
{
/* For verification purposes, display the Reading on the LCD */
LCD_Char_Position(0,0);
for(i=0;i<buffer_size;i++)
{
LCD_Char_PutChar(buffer[i]);
}
res = 1;
}
CyDelay(1000u/*ms*/);
LCD_Char_ClearDisplay();
CyDelay(1000u/*ms*/);
return res;
}
int main()
{
uint8 sample_segment1[WR_BUFFER_SIZE1] ;
initI2C();
for(;;)
{
Master_recieve_I2CData(I2C_SLAVE_ADDRESS, sample_segment1,
WR_BUFFER_SIZE1);
Master_send_I2CData(sample_segment1,
WR_BUFFER_SIZE1,I2C_SLAVE_ADDRESS1);
}
}
/* [] END OF FILE */
SLAVE (Receiver):
#include <project.h>
#define WR_BUFFER_SIZE (5u)
void recI2CData(uint8* buffer, uint8 buffer_size)
{
uint8 i = 0u;
uint8 byteCount = 0u;
/* Check if the slave buffer has been read */
if(I2CS_SlaveStatus() & I2CS_SSTAT_WR_CMPLT)
{
byteCount = I2CS_SlaveGetWriteBufSize();
I2CS_SlaveClearWriteStatus();
I2CS_SlaveClearWriteBuf();
/* If both bytes of the read buffer have been read */
if(byteCount == buffer_size)
{
/* Display data that was placed in the buffer on the
LCD and verify
* this is same as that received by master */
LCD_Char_Position(0,0);
for(i=0;i<buffer_size;i++)
{
LCD_Char_PutChar(buffer[i]);
}
}
else /* Wrong number of bytes read */
{
LCD_Char_ClearDisplay();
LCD_Char_PrintString("I2C Slave Error!");
}
}
CyDelay(1000u/*ms*/);
LCD_Char_ClearDisplay();
}
int main()
{
uint8 i = 0u;
uint8 byteCount = 0u;
uint8 sample_segment[WR_BUFFER_SIZE];
/* Set up slave read data buffer */
I2CS_SlaveInitWriteBuf((uint8 *) sample_segment, WR_BUFFER_SIZE);
LCD_Char_Start();
LCD_Char_PrintString("Slave Start");
CyDelay(1000u/*ms*/);
LCD_Char_ClearDisplay();
CyGlobalIntEnable;
I2CS_Start();
for(;;)
{
recI2CData(sample_segment,WR_BUFFER_SIZE);
}
} /* End of main */
SLAVE (Transmitter):
#include <device.h>
#define WR_BUFFER_SIZE (0x05u)
void recI2CData(uint8* buffer, uint8 buffer_size)
{
uint8 i = 0u;
uint8 byteCount = 0u;
if(I2CS_SlaveStatus() & I2CS_SSTAT_RD_CMPLT)
{
byteCount = I2CS_SlaveGetReadBufSize();
I2CS_SlaveClearReadStatus();
I2CS_SlaveClearReadBuf();
/* If both bytes of the read buffer have been read */
if(byteCount == buffer_size)
{
/* Display data that was placed in the buffer on the LCD and
verify
* this is same as that received by master */
LCD_Char_Position(0,0);
for(i=0;i<buffer_size;i++)
{
LCD_Char_PutChar(buffer[i]);
}
}
else /* Wrong number of bytes read */
{
LCD_Char_ClearDisplay();
LCD_Char_PrintString("I2C Slave Error!");
}
}
CyDelay(1000u/*ms*/);
LCD_Char_ClearDisplay();
CyDelay(1000u/*ms*/);
}
int main()
{
uint8 i = 0u;
uint8 byteCount = 0u;
uint8 sample_segment[WR_BUFFER_SIZE] = "12345";
/* Set up slave read data buffer */
LCD_Char_Start();
LCD_Char_PrintString("Slave Start");
CyDelay(1000u/*ms*/);
LCD_Char_ClearDisplay();
I2CS_SlaveInitReadBuf((uint8 *) sample_segment, WR_BUFFER_SIZE);
CyGlobalIntEnable;
I2CS_Start();
for(;;)
{
recI2CData(sample_segment, WR_BUFFER_SIZE);
}
} /* End of main */
Variable Length Transfer:
The following code allows data having variable length to be sent from the master to the slave. For this, a
custom protocol similar to the one used in implementing UART communication is used, where the data
is packetized and de-packetized at the transmitter and receiver respectively. The first byte in a data
packet holds the format specifier indicating the type of data that is being transmitted. The second byte
shows the length of the data block and the remaining byte consist of the actual data that is to be sent
(exact length corresponds to the value of the second byte). Unlike the protocol used for UART
communication however, there is no Cyclic Redundancy Check since I2C is a much more reliable
communication protocol and such checks would only introduce unnecessary overhead.
TRANSMITTER (Master):
#include <project.h>
#include <stdio.h>
uint8 sendI2CData(uint8* buffer, uint8 buffer_size, uint8 slave_address)
{
uint8 i = 0u;
uint8 temp;
uint8 res;
/* Attempt to initiate communication with the slave until the function
* completes without error.
*/
do
{
/* The syntax below automatically writes a buffer of data to a
slave
* device from start to stop.
*/
temp = I2CM_MasterWriteBuf(slave_address, buffer, buffer_size,
I2CM_MODE_COMPLETE_XFER);
}
while (temp != I2CM_MSTR_NO_ERROR);
/* Wait for the data transfer to complete */
while(I2CM_MasterStatus() & I2CM_MSTAT_XFER_INP);
temp = I2CM_MasterClearStatus();
/* If there is an error while transferring data */
if(temp & I2CM_MSTAT_ERR_XFER)
{
/* Indicate the error */
LCD_Char_PrintString("I2C Error! ");
CyDelay(2000u/*ms*/);
LCD_Char_ClearDisplay();
res = 0;
}
else /* Write completed without error */
{
/* For verification purposes, display the Reading on the LCD */
LCD_Char_Position(0,0);
for(i=2;i<buffer_size;i++)
{
LCD_Char_PutChar(buffer[i]);
}
res = 1;
}
CyDelay(1000u/*ms*/);
LCD_Char_ClearDisplay();
CyDelay(1000u/*ms*/);
return res;
}
int main()
{
uint8 sample_segment[7] = "00Mutee";
uint8 sample_segment2[5] = "00Ali";
sample_segment[0] = 1;
sample_segment[1] = 5;
sample_segment2[0] = 1;
sample_segment2[1] = 3;
LCD_Char_Start();
LCD_Char_PrintString("Master Start");
CyDelay(1000u/*ms*/);
LCD_Char_ClearDisplay();
CyGlobalIntEnable;
I2CM_Start();
for(;;)
{
sendI2CData(sample_segment, 7, 16u);
sendI2CData(sample_segment2, 5, 8u);
}
}
/* [] END OF FILE */
RECEIVER (Slave):
#include <project.h>
#define WR_BUFFER_SIZE (10u)
void recI2CData(uint8* buffer)
{
uint8 i = 0u;
uint8 byteCount = 0u;
/* Check if the slave buffer has been read */
if(I2CS_SlaveStatus() & I2CS_SSTAT_WR_CMPLT)
{
byteCount = I2CS_SlaveGetWriteBufSize();
I2CS_SlaveClearWriteStatus();
I2CS_SlaveClearWriteBuf();
/* If both bytes of the read buffer have been read */
if(buffer[0] >= 1)
{
/* Display data that was placed in the buffer on the
LCD and verify
* this is same as that received by master */
LCD_Char_Position(0,0);
for(i=2;i<buffer[1]+2;i++)
{
LCD_Char_PutChar(buffer[i]);
}
}
else /* Wrong number of bytes read */
{
LCD_Char_ClearDisplay();
LCD_Char_PrintString("I2C Slave Error!");
}
}
CyDelay(1000u/*ms*/);
LCD_Char_ClearDisplay();
//CyDelay(1000u/*ms*/);
}
int main()
{
uint8 i = 0u;
uint8 byteCount = 0u;
uint8 sample_segment[WR_BUFFER_SIZE];
/* Set up slave read data buffer */
I2CS_SlaveInitWriteBuf((uint8 *) sample_segment, WR_BUFFER_SIZE);
LCD_Char_Start();
LCD_Char_PrintString("Slave Start");
CyDelay(1000u/*ms*/);
LCD_Char_ClearDisplay();
CyGlobalIntEnable;
I2CS_Start();
for(;;)
{
recI2CData(sample_segment);
}
} /* End of main */
List of Functions Used and Their Description:
 void initI2C() – User defined function that initializes the I2C module, LCD and global interrupt
variables.
 uint8 Master_recieve_I2CData(uint8 slave_address, uint8* buffer, uint8 buffer_size) –
User defined function that receives a data in a buffer from a specified address.
 uint8 Master_send_I2CData(uint8* buffer, uint8 buffer_size, uint8 slave_address)-
A user defined function that transfers the data stored in the master buffer to a slave with a
specified address.
 I2CM_MasterReadBuf(slave_address, buffer, buffer_size, I2CM_MODE_COMPLETE_XFER)-
A built in function that reads a data from the slave and then stores it in a master buffer.
 I2CM_MasterWriteBuf(slave_address, buffer, buffer_size, I2CM_MODE_COMPLETE_XFER)-
A built in function that writes the referenced data buffer to a specified slave address.
 I2CM_MSTAT_ERR_XFER – A master status flag that shows whether error has occurred while
transferring data.
 I2CM_MasterStatus() – A built in function that returns the master’s status flags.
 I2CM_MasterClearStatus()-
A built in function that returns the master status and clears the status flags.
 void Slave_send_I2CData(uint8* buffer, uint8 buffer_size)-
A user defined function that enables slave to send data to a master.
 void Slave_rec_I2CData(uint8* buffer, uint8 buffer_size)-
A user defined function that enables slave to receive data from a master.
 I2CS_SlaveStatus() – A built in function that returns the slave’s status flags.
 I2CS_SlaveInitReadBuf((uint8 *) sample_segment, WR_BUFFER_SIZE) -
A built in function that sets up the slave receive data buffer.(master <-slave).
 I2CS_SlaveInitWriteBuf((uint8 *) sample_segment, WR_BUFFER_SIZE)-
A built in function that sets up the slave write buffer(master -> slave).
 I2CS_SlaveClearReadStatus()-
A built in function that returns the read status flags and clears the slave read status flags.
 I2CS_SlaveClearWriteStatus()-
A built in function that returns the write status flags and clears the slave write status flags.
Controller Area Network (CAN)
Introduction:
 Controller Area Network (CAN) is a serial communication protocol developed by Robert
Bosch GmbH in the early 1980s.
 This protocol was initially developed for automotive applications, for communications
between subsystems with no central control.
 CAN is also being adopted in areas such as embedded systems (CANOpen) and factory
automation (DeviceNet). CAN was standardized by the ISO in 2003.
This note introduces the basic concepts of the CAN protocol and demonstrates how
CAN bus communication can be implemented using PSoC.
Advantages of Using CAN:
CAN networks are designed for short messages with data length not more than 8 bytes, and a bit
rate up to 1 Mbps.
The CAN protocol offers several advantages over other serial communication protocols:
 CAN is a message-based protocol; CAN network nodes are not assigned specific addresses.
This provides flexibility to add or remove a node from the network without affecting the
rest of the network. In addition, if one of the nodes fails, the others continue to work and
communicate properly.
 CAN messages can be prioritized.
 CAN has five levels of error checking, to ensure reliable traffic and data integrity.
 The CAN network has system-wide data consistency, that is, if a message is corrupt at a
receiving node, the message is not accepted by any of the other receiving nodes.
 Corrupted messages are automatically retransmitted as soon as the bus is idle again.
Advantages of Implementing CAN using PSoC:
 Moreover, all PSoC Creator components, including the CAN component, can be easily
configured using a GUI, rather than writing C code for them.
 One of the main advantages of using PSoC is that being a development kit, it has many
useful peripherals already interfaced to it including key basic things like switches, LCD
display etc.
 In addition to this the coding is relatively straightforward with built in functions for most
operations and embedded C programming language with which we are already familiar.
 It also powered by a Cortex ARM M3 32-bit micro-controller which is a powerful tool for
high speed communication and fast data processing.
 On a more specific note to our project, PSoC development kits have built in controllers for
CAN communication and I2C making the interfacing of these protocols slightly easier and
negating the need to interface external peripherals. These protocols combined with built
in libraries make it easier for us to implement the protocol. Also one can connect it directly
to the computer to burn the code, hence no external hardware is required for that.
Choice of Hardware for CAN Protocol Implementation:
 For implementation of CAN communication, the PSoC manufacturer Cypress provides an
expansion module (C8YCKIT-017) for their development kit which takes care of voltage
level conversions and ensures reliable communication by providing a DB-9 connector to
connect the various nodes.
 However these expansion modules are a bit expensive and therefore we only intend to
acquire 3 of these and for final implementation, we intend to order 6 MCP-2551 ICs.
 The functionality of these ICs is similar to the PSoC expansion module, but they are much
more cost effective at the cost of requiring external circuitry (resistor, capacitor network)
and the need to be mounted on a PCB.
Basic Functioning of CAN:
The figure below shows how devices connect to a CAN bus. The CAN bus consists of two
physical lines, CANH and CANL. The CAN bus is typically terminated with a 120-Ω resistor at
each end.
The CAN bus carries differential signals, as the diagram below shows.
 A '1' is represented by both lines at approximately the same voltage (typically 2.5 V).
 A '0' is represented by a 1.5 V to 3 V difference in voltage between the lines.
 A ‘1’ is called a recessive bit and a ‘0’ is called a dominant bit.

The CAN protocol specifies that if recessive and dominant bits are simultaneously applied to the
CAN bus by two different nodes, the bus must have the dominant bit. This can be related to a wired
AND analogy – the bus does not have a recessive bit unless all nodes drive recessive bits. While in
an idle state, the bus holds recessive bits.
Data Transfer:
Any node can start transmitting a message when the CAN bus is idle. Messages are transmitted
through the bus in a fixed format called a frame. CAN defines four frame types:
 Data Frame - carries data from a transmitter to a receiver
 Remote frame - transmitted by a CAN node to request transmission of a data frame
 Error Frame - transmitted by any unit on detecting an error on the bus
 Overload Frame - used to provide extra delay between the preceding and the
succeeding data or remote frame.
Arbitration Field:
 The arbitration field of a data frame consists of two parts: an IDENTIFIER and a Remote
Transmission Request (RTR) bit.
 The identifier is used to describe the meaning of the data carried by the data frame. Each node
on the bus checks the identifier and decides whether to accept the message.
 Based on the length of the identifier field, two types of CAN messages are defined: standard and
extended.
 A standard CAN message has an 11-bit identifier and an extended CAN message has a 29-bit
identifier. Thus, a standard CAN data frame can support 211
different types of messages and an
extended CAN data frame can support 229
different messages.
 The RTR bit is used to indicate whether the given frame is a data frame or a remote frame. The
RTR bit is set ‘dominant’ in a data frame and ‘recessive’ in a remote frame.
Control Field:
 The control field of the data frame defines the number of data bytes in the data field. The
control field is six bits long, with four bits for data length and two bits reserved for future
expansion.
Data Field:
 The data field contains the data to be communicated.
CRC Field:
 The Cyclic Redundancy Check (CRC) field is for error checking. This field contains a bit sequence,
which is used to determine if the received frame contains any errors. The ACK field is used by the
receiving nodes to acknowledge that the frame was correctly received.
Hardware Implementation Connecting the CAN controller with
the PSoC:
 The PSoC outputs from the CAN component are TX and RX. You must level-translate these
outputs to obtain the CANH and CANL signals.
 The CY8CKIT-017 Expansion Board Kit is used as an external transceiver for level translation for
the examples given in this application note.
 CAN requires only two wires for a CAN bus. In the CY8CKIT-017, a standard male-to-male DB9
connector may be used to connect between two CAN nodes. The cable length between any two
CAN nodes in a CAN network should be restricted to the values shown in the table below.
Physical Connection (using C8YCKIT-017):
Functional Schematic (using MCP-2551 IC):
Below is a schematic diagram of the hardware interfacing required to connect a CAN compatible
microcontroller to a CAN bus. For simplicity, the hardware for only one PSoC device has been shown
since other devices can be attached to the bus in a similar fashion.
Functional Block Diagram:
Final Report

Final Report

  • 1.
    School of ElectricalEngineering and Computer Science PNSS-1 DATA HANDLING UNIT INTERNSHIP REPORT By: Ali Athar, Mutee ur Rehman, Armghan Ahmad Khan, Muneeb Zafar, Abdul Moiz, Mujtaba Hasan
  • 2.
    Data Handling Unit(DHU): General Description: The satellite will have different modules performing different functionality independently. Data Handling Unit is the part that will control and co-ordinate the information transmitted among these different modules. The Data Handling module will control the flow of information between ground station and all the modules of the satellite through the communication module. The data handling module is the gateway for incoming and outgoing telemetry and telecommand traffic. It will receive, validate, decode, and distribute commands from the ground, payload, or a subsystem to other subsystems. Functionalities: Following will be the set of functionality that the DHU will perform in the satellite.  The DHU will acquire and process the telemetry of all the subsystems.  The DHU will generate and process/transmit the on-board Tele-command to the relevant subsystems.  The DHU will perform the Autonomous Bus Management of the satellite.  The DHU of satellite will be capable of acquiring and processing the Direct Telemetry.  The DHU of satellite will be capable of generating and executing the Direct Tele-commands.  The DHU will perform the overall health monitoring of the satellite.  The DHU will provide the capability to transfer data between satellite subsystems, as needed using suitable communication protocols.  The DHU will allow simultaneous data collection and data downlink.  DHU will act as a master CAN controller.  The DHU will perform the CCSDS packetization for Telemetry and depacketization of Tele- command data, respectively.
  • 3.
     Where thereis no automatic recovery, sufficient telemetry data will be provided to enable ground detection of failures in a timely manner.  The DHU will be capable of decoding and validating the Tele-commands into time-tagged or event triggered sequences of derived commands throughout the mission life.  The DHU will distribute timing and synchronization signals to subsystems.  The DHU will generate onboard timing reference through on board GPS.  The DHU will have physical redundancy i.e. Nominal DHU and Redundant DHU in single housing and will operate in cold redundancy.  The DHU will be equipped with self-testing and diagnostic capability of all units and will interface upon start up or by ground command. Status of self-testing and diagnostic will be provided via telemetry to ground.  Cross-strapping will be provided so that redundant units can be switched in and out of the data bus and failed units can be isolated without affecting the subsystem performance and functionality.  Failure of any one unit will not disable any data transfer operation or any interface from operation.
  • 4.
    Functional Diagram ofDHU as part of overall satellite design:
  • 5.
    Universal Asynchronous Receiver Transmitter(UART) Introduction:  One of the oldest and most basic forms of serial communication.  Individual bits of a byte are taken and transmitted in a sequential fashion.  Supports full duplex communication  Each byte is preceded by a start bit and the byte is terminated using a stop bit at the end.  Minimal hardware is required. Salient Features of UART On PSoC:  Communication is made easier by user-friendly libraries and helper functions.  Baud rates of up to 115.2 kbits/s are supported.  Additional data integrity checks which are traditionally not used with UART are provided by the PSoC IDE such as 2 out of 3 voting per bit.  Numerous interrupts are provided by the platform which help in more efficient manipulation of data and enhance multi-tasking capabilities.  4-byte FIFO buffer is inserted between receiver/transmitter register and UART bus which gives the controller more time to service interrupts and reduces the likelihood of data loss. Hardware Connection: UART communication can be setup between two PSoC kits using a simple null modem connection. Since both devices operate on TTL standards, no MAX232 voltage converter circuit is required.
  • 6.
    Software Aspect ofCommunication:  UART is a very basic communication technique that simply sequentially transmits bits down a single line.  In order to be able to reliably communicate between two devices using UART, the user must define additional protocol features.  Our goal was to enable devices to transmit variable number of bytes using UART and a versatile, generic receiver that could handle such transmissions. On a specific note, the first major problem encountered was that unlike other protocols such as I2 C and CAN, UART transmission on its own provides no feedback as to whether the transmitted data was actually received or not. Therefore if we simply start to transmit an array via UART, there is no way to verify whether it was received or whether the receiving device was even powered on. To resolve this problem, we had to define a handshaking procedure of our own so that prior to any data being communicated, both transmitter and receiver can be sure that the other device is powered on and functional. The following flow diagram outlines the handshake procedure:
  • 7.
    Once the handshakeis complete, it is verified that both transmitter and receiver are ready to receive data. However, to achieve the versatility required, it was necessary to confine any data that had to be transmitted into packets and an algorithm that could de-packetize the data at the receiving end. It therefore became necessary to define a custom protocol that suited the requirements. The following protocol was hence implemented: To illustrate an example, suppose we are required to transmit an array consisting of 10 data elements of type character. Following is a breakdown of the packet that would be formed to transmit this data: 1st Byte: 0x01 (assuming character has been assigned an ID of 1) 2nd Byte: 0x0A (since 10 data elements have to be transmitted) 3rd Byte – 13th Byte: Data block of 10 bytes 14th Byte: Trailing byte consisting of Cyclic Redundancy Check code.
  • 8.
    Cyclic Redundancy Check(CRC): In order to verify the integrity of the data that was transmitted, a CRC code is generated using the entire data block that needs to be transmitted. This 8-bit (single byte) code is obtained by performing binary division on the array elements using a constant polynomial factor and the remainder left at the end is the required code. This code is transmitted along with the packet as shown above. At the receiving end, the device again generates a CRC code from the data it received and cross-checks this code against that which it has received. If the two match, then it is verified that the data was received correctly. Firmware Code: Below is the actual embedded C code which was used to achieve UART communication between two PSoC kits. The entire procedure is interrupt driven therefore it doesn’t keep the controller occupied. The initiation of communication can be done by calling a user-made function. The transmitter and receiver execute different codes, however these can very easily be combined to achieve bidirectional communication. RECEIVER: #define HANDSHAKE_VAR 255 #define ERROR_VAR 254 #define POLY 0xB2 uint8 isHSDone = 0; uint8 handshake_byte = 0; uint8 i=0; uint8 _formatSpecifier = 0; uint8 _packetSize = 0; uint8 _blockData = 0; uint8 _blcokDataCount = 0;
  • 9.
    uint8 _CRCbyte =0; uint8 dataBlock[10]; uint8 CRC; code unsigned char crc8_table[] = { 0x00, 0x3e, 0x7c, 0x42, 0xf8, 0xc6, 0x84, 0xba, 0x95, 0xab, 0xe9, 0xd7, 0x6d, 0x53, 0x11, 0x2f, 0x4f, 0x71, 0x33, 0x0d, 0xb7, 0x89, 0xcb, 0xf5, 0xda, 0xe4, 0xa6, 0x98, 0x22, 0x1c, 0x5e, 0x60, 0x9e, 0xa0, 0xe2, 0xdc, 0x66, 0x58, 0x1a, 0x24, 0x0b, 0x35, 0x77, 0x49, 0xf3, 0xcd, 0x8f, 0xb1, 0xd1, 0xef, 0xad, 0x93, 0x29, 0x17, 0x55, 0x6b, 0x44, 0x7a, 0x38, 0x06, 0xbc, 0x82, 0xc0, 0xfe, 0x59, 0x67, 0x25, 0x1b, 0xa1, 0x9f, 0xdd, 0xe3, 0xcc, 0xf2, 0xb0, 0x8e, 0x34, 0x0a, 0x48, 0x76, 0x16, 0x28, 0x6a, 0x54, 0xee, 0xd0, 0x92, 0xac, 0x83, 0xbd, 0xff, 0xc1, 0x7b, 0x45, 0x07, 0x39, 0xc7, 0xf9, 0xbb, 0x85, 0x3f, 0x01, 0x43, 0x7d, 0x52, 0x6c, 0x2e, 0x10, 0xaa, 0x94, 0xd6, 0xe8, 0x88, 0xb6, 0xf4, 0xca, 0x70, 0x4e, 0x0c, 0x32, 0x1d, 0x23, 0x61, 0x5f, 0xe5, 0xdb, 0x99, 0xa7, 0xb2, 0x8c, 0xce, 0xf0, 0x4a, 0x74, 0x36, 0x08, 0x27, 0x19, 0x5b, 0x65, 0xdf, 0xe1, 0xa3, 0x9d, 0xfd, 0xc3, 0x81, 0xbf, 0x05, 0x3b, 0x79, 0x47, 0x68, 0x56, 0x14, 0x2a, 0x90, 0xae, 0xec, 0xd2, 0x2c, 0x12, 0x50, 0x6e, 0xd4, 0xea, 0xa8, 0x96, 0xb9, 0x87, 0xc5, 0xfb, 0x41, 0x7f, 0x3d, 0x03, 0x63, 0x5d, 0x1f, 0x21, 0x9b, 0xa5, 0xe7, 0xd9, 0xf6, 0xc8, 0x8a, 0xb4, 0x0e, 0x30, 0x72, 0x4c,
  • 10.
    0xeb, 0xd5, 0x97,0xa9, 0x13, 0x2d, 0x6f, 0x51, 0x7e, 0x40, 0x02, 0x3c, 0x86, 0xb8, 0xfa, 0xc4, 0xa4, 0x9a, 0xd8, 0xe6, 0x5c, 0x62, 0x20, 0x1e, 0x31, 0x0f, 0x4d, 0x73, 0xc9, 0xf7, 0xb5, 0x8b, 0x75, 0x4b, 0x09, 0x37, 0x8d, 0xb3, 0xf1, 0xcf, 0xe0, 0xde, 0x9c, 0xa2, 0x18, 0x26, 0x64, 0x5a, 0x3a, 0x04, 0x46, 0x78, 0xc2, 0xfc, 0xbe, 0x80, 0xaf, 0x91, 0xd3, 0xed, 0x57, 0x69, 0x2b, 0x15 }; void initHandshake() { CYGlobalIntEnable; RX_ISR_Start(); RX_ISR_SetVector(RX_ISR_Interrupt); while (!isHSDone); LCD_Char_1_Position(0u, 5u); LCD_Char_1_PrintString("Sync Done"); } unsigned crc8_slow(unsigned crc, unsigned char *dataBlock, uint8 len) { unsigned char *end; if (len == 0) return crc; crc ^= 0xff; end = dataBlock + len; do { crc ^= *dataBlock++; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
  • 11.
    crc = crc& 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; } while (dataBlock < end); return crc ^ 0xff; } void printArray() { LCD_Char_1_Position(0u, 0u); LCD_Char_1_PrintHexUint8(dataBlock[0]); LCD_Char_1_PrintHexUint8(dataBlock[1]); LCD_Char_1_PrintHexUint8(CRC); LCD_Char_1_Position(1u, 0u); for(i=2; i<dataBlock[1]+2; i++) { LCD_Char_1_PrintHexUint8(dataBlock[i]); } } int main() { UART_1_Start(); LCD_Char_1_Start(); initHandshake(); for(;;) { }
  • 12.
    } void TX_ISR() { return; } void RX_ISR() { if(!isHSDone) { handshake_byte = UART_1_GetByte(); if (handshake_byte == HANDSHAKE_VAR) UART_1_PutChar(HANDSHAKE_VAR); isHSDone = 1; _formatSpecifier = 1; return; } else if (_formatSpecifier) { dataBlock[i] = UART_1_GetChar(); i++; _formatSpecifier = 0; _packetSize = 1; } else if (_packetSize) { dataBlock[i] = UART_1_GetChar(); i++; _packetSize = 0; _blockData = 1; }
  • 13.
    else if (_blockData) { dataBlock[i]= UART_1_GetChar(); i++; if (i == dataBlock[1]+2) { _blockData = 0; _CRCbyte = 1; } } else if (_CRCbyte) { CRC = UART_1_GetChar(); if (CRC == crc8_slow(0, dataBlock, dataBlock[1]+2)) { CYGlobalIntDisable; printArray(); } else { CYGlobalIntDisable; LCD_Char_1_Position(1u, 8u); LCD_Char_1_PrintString("CRC err"); printArray(); } _CRCbyte = 0; } }
  • 14.
    TRANSMITTER: #define HANDSHAKE_VAR 255 #definePOLY 0xB2 #define packetSize 5 uint8 isHSDone = 0; uint8 handshake_byte = 0; uint8 TxByte=0; code unsigned char crc8_table[] = { 0x00, 0x3e, 0x7c, 0x42, 0xf8, 0xc6, 0x84, 0xba, 0x95, 0xab, 0xe9, 0xd7, 0x6d, 0x53, 0x11, 0x2f, 0x4f, 0x71, 0x33, 0x0d, 0xb7, 0x89, 0xcb, 0xf5, 0xda, 0xe4, 0xa6, 0x98, 0x22, 0x1c, 0x5e, 0x60, 0x9e, 0xa0, 0xe2, 0xdc, 0x66, 0x58, 0x1a, 0x24, 0x0b, 0x35, 0x77, 0x49, 0xf3, 0xcd, 0x8f, 0xb1, 0xd1, 0xef, 0xad, 0x93, 0x29, 0x17, 0x55, 0x6b, 0x44, 0x7a, 0x38, 0x06, 0xbc, 0x82, 0xc0, 0xfe, 0x59, 0x67, 0x25, 0x1b, 0xa1, 0x9f, 0xdd, 0xe3, 0xcc, 0xf2, 0xb0, 0x8e, 0x34, 0x0a, 0x48, 0x76, 0x16, 0x28, 0x6a, 0x54, 0xee, 0xd0, 0x92, 0xac, 0x83, 0xbd, 0xff, 0xc1, 0x7b, 0x45, 0x07, 0x39, 0xc7, 0xf9, 0xbb, 0x85, 0x3f, 0x01, 0x43, 0x7d, 0x52, 0x6c, 0x2e, 0x10, 0xaa, 0x94, 0xd6, 0xe8, 0x88, 0xb6, 0xf4, 0xca, 0x70, 0x4e, 0x0c, 0x32, 0x1d, 0x23, 0x61, 0x5f, 0xe5, 0xdb, 0x99, 0xa7, 0xb2, 0x8c, 0xce, 0xf0, 0x4a, 0x74, 0x36, 0x08, 0x27, 0x19, 0x5b, 0x65, 0xdf, 0xe1, 0xa3, 0x9d, 0xfd, 0xc3, 0x81, 0xbf, 0x05, 0x3b, 0x79, 0x47, 0x68, 0x56, 0x14, 0x2a,
  • 15.
    0x90, 0xae, 0xec,0xd2, 0x2c, 0x12, 0x50, 0x6e, 0xd4, 0xea, 0xa8, 0x96, 0xb9, 0x87, 0xc5, 0xfb, 0x41, 0x7f, 0x3d, 0x03, 0x63, 0x5d, 0x1f, 0x21, 0x9b, 0xa5, 0xe7, 0xd9, 0xf6, 0xc8, 0x8a, 0xb4, 0x0e, 0x30, 0x72, 0x4c, 0xeb, 0xd5, 0x97, 0xa9, 0x13, 0x2d, 0x6f, 0x51, 0x7e, 0x40, 0x02, 0x3c, 0x86, 0xb8, 0xfa, 0xc4, 0xa4, 0x9a, 0xd8, 0xe6, 0x5c, 0x62, 0x20, 0x1e, 0x31, 0x0f, 0x4d, 0x73, 0xc9, 0xf7, 0xb5, 0x8b, 0x75, 0x4b, 0x09, 0x37, 0x8d, 0xb3, 0xf1, 0xcf, 0xe0, 0xde, 0x9c, 0xa2, 0x18, 0x26, 0x64, 0x5a, 0x3a, 0x04, 0x46, 0x78, 0xc2, 0xfc, 0xbe, 0x80, 0xaf, 0x91, 0xd3, 0xed, 0x57, 0x69, 0x2b, 0x15 }; uint8 array[7] = {1, 5, 10, 11, 12, 13, 14}; void initHandshake() { UART_1_PutChar(HANDSHAKE_VAR); RX_ISR_Start(); RX_ISR_SetVector(RX_ISR_Interrupt); while (handshake_byte != HANDSHAKE_VAR); isHSDone = 1; LCD_Char_1_Position(0u, 5u); LCD_Char_1_PrintString("Sync Done"); } unsigned crc8_slow(unsigned crc, unsigned char *dataBlock, uint8 len) { unsigned char *end;
  • 16.
    if (len ==0) return crc; crc ^= 0xff; end = dataBlock + len; do { crc ^= *dataBlock++; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1; } while (dataBlock < end); return crc ^ 0xff; } int main() { uint8 i; uint8 CRC; CYGlobalIntDisable; UART_1_Start(); LCD_Char_1_Start(); TX_ISR_Start(); TX_ISR_SetVector(TX_ISR_Interrupt); CYGlobalIntEnable; initHandshake();
  • 17.
    CRC = crc8_slow(0,array, packetSize+2); for(i=0; i<7; i++) { UART_1_PutChar(array[i]); } UART_1_PutChar(CRC); while(1) { LCD_Char_1_Position(0u, 0u); LCD_Char_1_PrintString("HS/Array Out"); LCD_Char_1_Position(1u, 0u); LCD_Char_1_PrintHexUint8(CRC); } } void TX_ISR() { return; } void RX_ISR() { if (!isHSDone) { handshake_byte = UART_1_GetByte(); } else { } }
  • 18.
    Inter-Integrated Circuit (I2C) GeneralDescription: I2 C (also known as IIC) stands for Inter-Integrated Circuit. It is a multi-master multi-slave serial computer bus. The I2C bus is an industry-standard, two-wire hardware interface developed by Philips. It is used to connect low speed peripherals with mother-board. I²C uses only two bidirectional open-drain lines, Serial Data Line (SDA) and Serial Clock Line (SCL) which are pulled up with resistors. The typical voltage levels are +5 V or +3.3 V. The master initiates all communication on the I2C bus and supplies the clock for all slave devices. Each I2C bus has a 7-bit address space for different slaves. Each device connected to the bus has got a unique address. Data divided into 8-bit bytes. A few control bits for controlling the communication start, end, direction and for an acknowledgment mechanism.. The I2C component supports standard clock speeds up to 1000 kbps. Hardware Connections: Nodes : There are two types of nodes in I2C bus  Master node —that generates the clock signal and starts communication with slaves  Slave node — node that receives the clock and responds when addressed by the master
  • 19.
    Modes of operation: Thereare four possible modes of operation for a given bus device:  master transmit — master is sending data to a slave  master receive — master is receiving data from a slave  slave transmit — slave is sending data to the master  slave receive — slave is receiving data from the master I2C and PSOC development kit:  Supports Slave, Master, Multi-Master and Multi-Master-Slave operation  Only two pins (SDA and SCL) required to interface to I2C bus  Standard data rates of 100/400/1000 kbps supported  High level APIs require minimal user programming Data Rate This parameter is used to set the I2C data rate value up to 1000 kbps; the actual speed may differ based on available clock speed and divider range. The standard speeds are 50, 100 (default), 400, and 1000 kbps. Pin Connections: SCL P12[0] SDA P12[1] Master Operation: When the master wants to transmit something it starts it by sending a start bit followed by the 7-bit address of the slave it wishes to communicate with, which is finally followed by a single bit representing whether it wishes to write(0) to or read(1) from the slave.
  • 20.
    Slave Operation: If theslave exists on the bus then it will respond with an acknowledgement signal an ACK bit. The master then continues in either transmit or receive mode (according to the read/write bit it sent), and the slave continues in its complementary mode (receive or transmit, respectively). The address and the data bytes are sent Big Endian Format. The start bit is indicated by a high-to-low transition of SDA with SCL high; the stop bit is indicated by a low-to-high transition of SDA with SCL high. All other transitions of SDA take place with SCL low. If the master wishes to write to the slave then it repeatedly sends a byte with the slave sending an ACK bit. (In this situation, the master is in master transmit mode and the slave is in slave receive mode.) If the master wishes to read from the slave then it repeatedly receives a byte from the slave, the master sending an ACK bit after every byte but the last one. (In this situation, the master is in master receive mode and the slave is in slave transmit mode.) The master then either ends transmission with a stop bit. Timing Diagram: Over the course of the internship, we managed to come up with three different methods of communication using I2C protocol. Each of these is elaborated below with the full code pasted: 2D Array Transmission Image data is represented in a 2D array form. Typically, the images taken by the payload of the satellite have a resolution of 796x800. Such a large image cannot be transmitted all at once and hence the general approach is to break such a large array into numerous, smaller arrays and transmit these segments one-by-one. As an example, the code below transmits a 4x4 matrix which is first broken into four 2x2 matrices before transmission. The size of the 2D array and that of the smaller segment can easily be changed by altering the iterator limits in the nested loops in the code below:
  • 21.
    TRANSMITTER (Master): #include <project.h> #include<stdio.h> #define I2C_SLAVE_ADDRESS (0x10u) #define I2C_SLAVE_ADDRESS1 (0x08u) #define WR_BUFFER_SIZE1 (0x05u) #define WR_BUFFER_SIZE2 (0x05u) void initI2C() { LCD_Char_Start(); LCD_Char_PrintString("Master Start"); CyDelay(1000u); LCD_Char_ClearDisplay(); CyGlobalIntEnable; I2CM_Start(); } uint8 Master_send_I2CData(uint8* buffer, uint8 buffer_size, uint8 slave_address) { uint8 i = 0u; uint8 temp; uint8 res; /* Attempt to initiate communication with the slave until the function * completes without error. */ do { /* The syntax below automatically writes a buffer of data to a slave * device from start to stop. */ temp = I2CM_MasterWriteBuf(slave_address, buffer, buffer_size, I2CM_MODE_COMPLETE_XFER); } while (temp != I2CM_MSTR_NO_ERROR); /* Wait for the data transfer to complete */ while(I2CM_MasterStatus() & I2CM_MSTAT_XFER_INP); temp = I2CM_MasterClearStatus(); /* If there is an error while transferring data */ if(temp & I2CM_MSTAT_ERR_XFER) { /* Indicate the error */ LCD_Char_PrintString("I2C Error! "); CyDelay(2000u/*ms*/); LCD_Char_ClearDisplay(); res = 0; }
  • 22.
    else /* Writecompleted without error */ { res = 1; } LCD_Char_ClearDisplay(); return res; } int main() { uint8 x, i, j, k, a; uint8 array[4][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; uint8 segment[4]; initI2C(); for(;;) { int z = 0; for (x = 0; x < 3; x += 2) { for (i = 0; i < 3; i += 2) { for (j = 0; j < 2; j++) { for (k = 0; k < 2; k++) { segment[z] = array[x + j][i + k]; z++; } } Master_send_I2CData(segment, 4, 8); a=0; for(j=0;j<2;j++) { for (k=0; k<2; k++) { LCD_Char_Position(j, k*3); LCD_Char_PrintHexUint8(segment[a]); a++; } } z = 0; CyDelay(2000); } } }
  • 23.
    } /* [] ENDOF FILE */ RECEIVER (Slave): #include <project.h> #define WR_BUFFER_SIZE (4u) void recI2CData(uint8* buffer, uint8 buffer_size) { uint8 i = 0u; uint8 j, k; uint8 byteCount = 0u; /* Check if the slave buffer has been read */ if(I2CS_SlaveStatus() & I2CS_SSTAT_WR_CMPLT) { byteCount = I2CS_SlaveGetWriteBufSize(); I2CS_SlaveClearWriteStatus(); I2CS_SlaveClearWriteBuf(); /* If both bytes of the read buffer have been read */ if(byteCount == buffer_size) { /* Display data that was placed in the buffer on the LCD and verify * this is same as that received by master */ LCD_Char_Position(0,0); for(j=0;j<2;j++) { for (k=0; k<2; k++) { LCD_Char_Position(j, k*3); LCD_Char_PrintHexUint8(buffer[i]); i++; } } } else /* Wrong number of bytes read */ { LCD_Char_ClearDisplay(); LCD_Char_PrintString("I2C Slave Error!"); }
  • 24.
    } } int main() { uint8 i= 0u; uint8 byteCount = 0u; uint8 sample_segment[WR_BUFFER_SIZE]; /* Set up slave read data buffer */ I2CS_SlaveInitWriteBuf((uint8 *) sample_segment, WR_BUFFER_SIZE); LCD_Char_Start(); LCD_Char_PrintString("Slave Start"); CyDelay(1000u/*ms*/); LCD_Char_ClearDisplay(); CyGlobalIntEnable; I2CS_Start(); for(;;) { recI2CData(sample_segment,WR_BUFFER_SIZE); } } /* End of main */ /* [] END OF FILE */ Bidirectional Data Transfer: Master receives an array from one slave device with a specified address and then sends the data to another slave having another slave address (indirect slave-to-slave communication). MASTER (Transmitter/Receiver): #include <project.h> #include <stdio.h> #define I2C_SLAVE_ADDRESS (0x10u) #define I2C_SLAVE_ADDRESS1 (0x08u) #define WR_BUFFER_SIZE1 (0x05u) #define WR_BUFFER_SIZE2 (0x05u)
  • 25.
    void initI2C() { LCD_Char_Start(); LCD_Char_PrintString("Master Start"); CyDelay(1000u); LCD_Char_ClearDisplay(); CyGlobalIntEnable; I2CM_Start(); } uint8Master_recieve_I2CData(uint8 slave_address, uint8* buffer, uint8 buffer_size) { uint8 i = 0u; uint8 temp; uint8 res; do { /* The syntax below automatically writes a buffer of data to a slave * device from start to stop. */ temp = I2CM_MasterReadBuf(slave_address, buffer, buffer_size, I2CM_MODE_COMPLETE_XFER); } while (temp != I2CM_MSTR_NO_ERROR); /* Wait for the data transfer to complete */ while(I2CM_MasterStatus() & I2CM_MSTAT_XFER_INP); temp = I2CM_MasterClearStatus(); /* If there is an error while transferring data */ if (temp & I2CM_MSTAT_ERR_XFER) { /* Indicate the error */ res = 0; LCD_Char_PrintString("I2C Error! "); CyDelay(2000u/*ms*/); LCD_Char_ClearDisplay(); } else /* Write completed without error */ { /* For verification purposes, display the Reading on the LCD */ res = 1; LCD_Char_Position(0,0); for(i=0;i<buffer_size;i++) { LCD_Char_PutChar(buffer[i]); //CyDelay(100u/*ms*/); } }
  • 26.
    /* Delay introducedfor ease of reading LCD */ CyDelay(1000u/*ms*/); LCD_Char_ClearDisplay(); CyDelay(1000u/*ms*/); return res; } uint8 Master_send_I2CData(uint8* buffer, uint8 buffer_size, uint8 slave_address) { uint8 i = 0u; uint8 temp; uint8 res; /* Attempt to initiate communication with the slave until the function * completes without error. */ do { /* The syntax below automatically writes a buffer of data to a slave * device from start to stop. */ temp = I2CM_MasterWriteBuf(slave_address, buffer, buffer_size, I2CM_MODE_COMPLETE_XFER); } while (temp != I2CM_MSTR_NO_ERROR); /* Wait for the data transfer to complete */ while(I2CM_MasterStatus() & I2CM_MSTAT_XFER_INP); temp = I2CM_MasterClearStatus(); /* If there is an error while transferring data */ if(temp & I2CM_MSTAT_ERR_XFER) { /* Indicate the error */ LCD_Char_PrintString("I2C Error! "); CyDelay(2000u/*ms*/); LCD_Char_ClearDisplay(); res = 0; } else /* Write completed without error */ { /* For verification purposes, display the Reading on the LCD */ LCD_Char_Position(0,0); for(i=0;i<buffer_size;i++) { LCD_Char_PutChar(buffer[i]); } res = 1; } CyDelay(1000u/*ms*/); LCD_Char_ClearDisplay();
  • 27.
    CyDelay(1000u/*ms*/); return res; } int main() { uint8sample_segment1[WR_BUFFER_SIZE1] ; initI2C(); for(;;) { Master_recieve_I2CData(I2C_SLAVE_ADDRESS, sample_segment1, WR_BUFFER_SIZE1); Master_send_I2CData(sample_segment1, WR_BUFFER_SIZE1,I2C_SLAVE_ADDRESS1); } } /* [] END OF FILE */ SLAVE (Receiver): #include <project.h> #define WR_BUFFER_SIZE (5u) void recI2CData(uint8* buffer, uint8 buffer_size) { uint8 i = 0u; uint8 byteCount = 0u; /* Check if the slave buffer has been read */ if(I2CS_SlaveStatus() & I2CS_SSTAT_WR_CMPLT) { byteCount = I2CS_SlaveGetWriteBufSize(); I2CS_SlaveClearWriteStatus(); I2CS_SlaveClearWriteBuf(); /* If both bytes of the read buffer have been read */ if(byteCount == buffer_size) { /* Display data that was placed in the buffer on the LCD and verify * this is same as that received by master */ LCD_Char_Position(0,0); for(i=0;i<buffer_size;i++)
  • 28.
    { LCD_Char_PutChar(buffer[i]); } } else /* Wrongnumber of bytes read */ { LCD_Char_ClearDisplay(); LCD_Char_PrintString("I2C Slave Error!"); } } CyDelay(1000u/*ms*/); LCD_Char_ClearDisplay(); } int main() { uint8 i = 0u; uint8 byteCount = 0u; uint8 sample_segment[WR_BUFFER_SIZE]; /* Set up slave read data buffer */ I2CS_SlaveInitWriteBuf((uint8 *) sample_segment, WR_BUFFER_SIZE); LCD_Char_Start(); LCD_Char_PrintString("Slave Start"); CyDelay(1000u/*ms*/); LCD_Char_ClearDisplay(); CyGlobalIntEnable; I2CS_Start(); for(;;) { recI2CData(sample_segment,WR_BUFFER_SIZE); } } /* End of main */ SLAVE (Transmitter): #include <device.h> #define WR_BUFFER_SIZE (0x05u) void recI2CData(uint8* buffer, uint8 buffer_size) { uint8 i = 0u; uint8 byteCount = 0u; if(I2CS_SlaveStatus() & I2CS_SSTAT_RD_CMPLT) {
  • 29.
    byteCount = I2CS_SlaveGetReadBufSize(); I2CS_SlaveClearReadStatus(); I2CS_SlaveClearReadBuf(); /*If both bytes of the read buffer have been read */ if(byteCount == buffer_size) { /* Display data that was placed in the buffer on the LCD and verify * this is same as that received by master */ LCD_Char_Position(0,0); for(i=0;i<buffer_size;i++) { LCD_Char_PutChar(buffer[i]); } } else /* Wrong number of bytes read */ { LCD_Char_ClearDisplay(); LCD_Char_PrintString("I2C Slave Error!"); } } CyDelay(1000u/*ms*/); LCD_Char_ClearDisplay(); CyDelay(1000u/*ms*/); } int main() { uint8 i = 0u; uint8 byteCount = 0u; uint8 sample_segment[WR_BUFFER_SIZE] = "12345"; /* Set up slave read data buffer */ LCD_Char_Start(); LCD_Char_PrintString("Slave Start"); CyDelay(1000u/*ms*/); LCD_Char_ClearDisplay(); I2CS_SlaveInitReadBuf((uint8 *) sample_segment, WR_BUFFER_SIZE); CyGlobalIntEnable; I2CS_Start(); for(;;) { recI2CData(sample_segment, WR_BUFFER_SIZE); } } /* End of main */
  • 30.
    Variable Length Transfer: Thefollowing code allows data having variable length to be sent from the master to the slave. For this, a custom protocol similar to the one used in implementing UART communication is used, where the data is packetized and de-packetized at the transmitter and receiver respectively. The first byte in a data packet holds the format specifier indicating the type of data that is being transmitted. The second byte shows the length of the data block and the remaining byte consist of the actual data that is to be sent (exact length corresponds to the value of the second byte). Unlike the protocol used for UART communication however, there is no Cyclic Redundancy Check since I2C is a much more reliable communication protocol and such checks would only introduce unnecessary overhead. TRANSMITTER (Master): #include <project.h> #include <stdio.h> uint8 sendI2CData(uint8* buffer, uint8 buffer_size, uint8 slave_address) { uint8 i = 0u; uint8 temp; uint8 res; /* Attempt to initiate communication with the slave until the function * completes without error. */ do { /* The syntax below automatically writes a buffer of data to a slave * device from start to stop. */ temp = I2CM_MasterWriteBuf(slave_address, buffer, buffer_size, I2CM_MODE_COMPLETE_XFER); } while (temp != I2CM_MSTR_NO_ERROR); /* Wait for the data transfer to complete */ while(I2CM_MasterStatus() & I2CM_MSTAT_XFER_INP); temp = I2CM_MasterClearStatus(); /* If there is an error while transferring data */ if(temp & I2CM_MSTAT_ERR_XFER) { /* Indicate the error */ LCD_Char_PrintString("I2C Error! "); CyDelay(2000u/*ms*/); LCD_Char_ClearDisplay(); res = 0;
  • 31.
    } else /* Writecompleted without error */ { /* For verification purposes, display the Reading on the LCD */ LCD_Char_Position(0,0); for(i=2;i<buffer_size;i++) { LCD_Char_PutChar(buffer[i]); } res = 1; } CyDelay(1000u/*ms*/); LCD_Char_ClearDisplay(); CyDelay(1000u/*ms*/); return res; } int main() { uint8 sample_segment[7] = "00Mutee"; uint8 sample_segment2[5] = "00Ali"; sample_segment[0] = 1; sample_segment[1] = 5; sample_segment2[0] = 1; sample_segment2[1] = 3; LCD_Char_Start(); LCD_Char_PrintString("Master Start"); CyDelay(1000u/*ms*/); LCD_Char_ClearDisplay(); CyGlobalIntEnable; I2CM_Start(); for(;;) { sendI2CData(sample_segment, 7, 16u); sendI2CData(sample_segment2, 5, 8u); } } /* [] END OF FILE */
  • 32.
    RECEIVER (Slave): #include <project.h> #defineWR_BUFFER_SIZE (10u) void recI2CData(uint8* buffer) { uint8 i = 0u; uint8 byteCount = 0u; /* Check if the slave buffer has been read */ if(I2CS_SlaveStatus() & I2CS_SSTAT_WR_CMPLT) { byteCount = I2CS_SlaveGetWriteBufSize(); I2CS_SlaveClearWriteStatus(); I2CS_SlaveClearWriteBuf(); /* If both bytes of the read buffer have been read */ if(buffer[0] >= 1) { /* Display data that was placed in the buffer on the LCD and verify * this is same as that received by master */ LCD_Char_Position(0,0); for(i=2;i<buffer[1]+2;i++) { LCD_Char_PutChar(buffer[i]); } } else /* Wrong number of bytes read */ { LCD_Char_ClearDisplay(); LCD_Char_PrintString("I2C Slave Error!"); } } CyDelay(1000u/*ms*/); LCD_Char_ClearDisplay(); //CyDelay(1000u/*ms*/); } int main() { uint8 i = 0u; uint8 byteCount = 0u; uint8 sample_segment[WR_BUFFER_SIZE]; /* Set up slave read data buffer */
  • 33.
    I2CS_SlaveInitWriteBuf((uint8 *) sample_segment,WR_BUFFER_SIZE); LCD_Char_Start(); LCD_Char_PrintString("Slave Start"); CyDelay(1000u/*ms*/); LCD_Char_ClearDisplay(); CyGlobalIntEnable; I2CS_Start(); for(;;) { recI2CData(sample_segment); } } /* End of main */ List of Functions Used and Their Description:  void initI2C() – User defined function that initializes the I2C module, LCD and global interrupt variables.  uint8 Master_recieve_I2CData(uint8 slave_address, uint8* buffer, uint8 buffer_size) – User defined function that receives a data in a buffer from a specified address.  uint8 Master_send_I2CData(uint8* buffer, uint8 buffer_size, uint8 slave_address)- A user defined function that transfers the data stored in the master buffer to a slave with a specified address.  I2CM_MasterReadBuf(slave_address, buffer, buffer_size, I2CM_MODE_COMPLETE_XFER)- A built in function that reads a data from the slave and then stores it in a master buffer.  I2CM_MasterWriteBuf(slave_address, buffer, buffer_size, I2CM_MODE_COMPLETE_XFER)- A built in function that writes the referenced data buffer to a specified slave address.  I2CM_MSTAT_ERR_XFER – A master status flag that shows whether error has occurred while transferring data.  I2CM_MasterStatus() – A built in function that returns the master’s status flags.  I2CM_MasterClearStatus()- A built in function that returns the master status and clears the status flags.
  • 34.
     void Slave_send_I2CData(uint8*buffer, uint8 buffer_size)- A user defined function that enables slave to send data to a master.  void Slave_rec_I2CData(uint8* buffer, uint8 buffer_size)- A user defined function that enables slave to receive data from a master.  I2CS_SlaveStatus() – A built in function that returns the slave’s status flags.  I2CS_SlaveInitReadBuf((uint8 *) sample_segment, WR_BUFFER_SIZE) - A built in function that sets up the slave receive data buffer.(master <-slave).  I2CS_SlaveInitWriteBuf((uint8 *) sample_segment, WR_BUFFER_SIZE)- A built in function that sets up the slave write buffer(master -> slave).  I2CS_SlaveClearReadStatus()- A built in function that returns the read status flags and clears the slave read status flags.  I2CS_SlaveClearWriteStatus()- A built in function that returns the write status flags and clears the slave write status flags.
  • 35.
    Controller Area Network(CAN) Introduction:  Controller Area Network (CAN) is a serial communication protocol developed by Robert Bosch GmbH in the early 1980s.  This protocol was initially developed for automotive applications, for communications between subsystems with no central control.  CAN is also being adopted in areas such as embedded systems (CANOpen) and factory automation (DeviceNet). CAN was standardized by the ISO in 2003. This note introduces the basic concepts of the CAN protocol and demonstrates how CAN bus communication can be implemented using PSoC. Advantages of Using CAN: CAN networks are designed for short messages with data length not more than 8 bytes, and a bit rate up to 1 Mbps. The CAN protocol offers several advantages over other serial communication protocols:  CAN is a message-based protocol; CAN network nodes are not assigned specific addresses. This provides flexibility to add or remove a node from the network without affecting the rest of the network. In addition, if one of the nodes fails, the others continue to work and communicate properly.  CAN messages can be prioritized.  CAN has five levels of error checking, to ensure reliable traffic and data integrity.  The CAN network has system-wide data consistency, that is, if a message is corrupt at a receiving node, the message is not accepted by any of the other receiving nodes.  Corrupted messages are automatically retransmitted as soon as the bus is idle again.
  • 36.
    Advantages of ImplementingCAN using PSoC:  Moreover, all PSoC Creator components, including the CAN component, can be easily configured using a GUI, rather than writing C code for them.  One of the main advantages of using PSoC is that being a development kit, it has many useful peripherals already interfaced to it including key basic things like switches, LCD display etc.  In addition to this the coding is relatively straightforward with built in functions for most operations and embedded C programming language with which we are already familiar.  It also powered by a Cortex ARM M3 32-bit micro-controller which is a powerful tool for high speed communication and fast data processing.  On a more specific note to our project, PSoC development kits have built in controllers for CAN communication and I2C making the interfacing of these protocols slightly easier and negating the need to interface external peripherals. These protocols combined with built in libraries make it easier for us to implement the protocol. Also one can connect it directly to the computer to burn the code, hence no external hardware is required for that. Choice of Hardware for CAN Protocol Implementation:  For implementation of CAN communication, the PSoC manufacturer Cypress provides an expansion module (C8YCKIT-017) for their development kit which takes care of voltage level conversions and ensures reliable communication by providing a DB-9 connector to connect the various nodes.  However these expansion modules are a bit expensive and therefore we only intend to acquire 3 of these and for final implementation, we intend to order 6 MCP-2551 ICs.  The functionality of these ICs is similar to the PSoC expansion module, but they are much more cost effective at the cost of requiring external circuitry (resistor, capacitor network) and the need to be mounted on a PCB.
  • 37.
    Basic Functioning ofCAN: The figure below shows how devices connect to a CAN bus. The CAN bus consists of two physical lines, CANH and CANL. The CAN bus is typically terminated with a 120-Ω resistor at each end. The CAN bus carries differential signals, as the diagram below shows.  A '1' is represented by both lines at approximately the same voltage (typically 2.5 V).  A '0' is represented by a 1.5 V to 3 V difference in voltage between the lines.  A ‘1’ is called a recessive bit and a ‘0’ is called a dominant bit. 
  • 38.
    The CAN protocolspecifies that if recessive and dominant bits are simultaneously applied to the CAN bus by two different nodes, the bus must have the dominant bit. This can be related to a wired AND analogy – the bus does not have a recessive bit unless all nodes drive recessive bits. While in an idle state, the bus holds recessive bits. Data Transfer: Any node can start transmitting a message when the CAN bus is idle. Messages are transmitted through the bus in a fixed format called a frame. CAN defines four frame types:  Data Frame - carries data from a transmitter to a receiver  Remote frame - transmitted by a CAN node to request transmission of a data frame  Error Frame - transmitted by any unit on detecting an error on the bus  Overload Frame - used to provide extra delay between the preceding and the succeeding data or remote frame.
  • 39.
    Arbitration Field:  Thearbitration field of a data frame consists of two parts: an IDENTIFIER and a Remote Transmission Request (RTR) bit.  The identifier is used to describe the meaning of the data carried by the data frame. Each node on the bus checks the identifier and decides whether to accept the message.  Based on the length of the identifier field, two types of CAN messages are defined: standard and extended.  A standard CAN message has an 11-bit identifier and an extended CAN message has a 29-bit identifier. Thus, a standard CAN data frame can support 211 different types of messages and an extended CAN data frame can support 229 different messages.  The RTR bit is used to indicate whether the given frame is a data frame or a remote frame. The RTR bit is set ‘dominant’ in a data frame and ‘recessive’ in a remote frame. Control Field:  The control field of the data frame defines the number of data bytes in the data field. The control field is six bits long, with four bits for data length and two bits reserved for future expansion. Data Field:  The data field contains the data to be communicated.
  • 40.
    CRC Field:  TheCyclic Redundancy Check (CRC) field is for error checking. This field contains a bit sequence, which is used to determine if the received frame contains any errors. The ACK field is used by the receiving nodes to acknowledge that the frame was correctly received. Hardware Implementation Connecting the CAN controller with the PSoC:  The PSoC outputs from the CAN component are TX and RX. You must level-translate these outputs to obtain the CANH and CANL signals.  The CY8CKIT-017 Expansion Board Kit is used as an external transceiver for level translation for the examples given in this application note.  CAN requires only two wires for a CAN bus. In the CY8CKIT-017, a standard male-to-male DB9 connector may be used to connect between two CAN nodes. The cable length between any two CAN nodes in a CAN network should be restricted to the values shown in the table below.
  • 41.
  • 42.
    Functional Schematic (usingMCP-2551 IC): Below is a schematic diagram of the hardware interfacing required to connect a CAN compatible microcontroller to a CAN bus. For simplicity, the hardware for only one PSoC device has been shown since other devices can be attached to the bus in a similar fashion.
  • 43.