/**************************************************************************//**
* @file CEC_Lib.c
* @brief CEC protocal stack
* Transmit, receive and retransmit CEC data
* @note
* Copyright (C) 2019 Nuvoton Technology Corp. All rights reserved.
*
*****************************************************************************/
#include <stdio.h>
#include <stdint.h>
#include "M051Series.h"
#include "Smpl_CEC.h"
uint8_t au8CECTxData[CECMaxPayload], au8CECRxData[CECMaxPayload];
//define
//au8CECTxData[0]--header
//au8CECTxData[1]--data1
//...
volatile uint8_t u8CECTxLength;
uint8_t u8CECState, u8CECTRStatus, u8CECLogicalAddr, u8CECPhysicalAddr,
u8CECTxRemain, u8CECRxLength, u8CECEOM, u8CECGetData;
uint8_t u8CECEIntStatus, u8CECTimerCNT, u8CECRetranCNT, u8CECSubCNT, u8CECSrcAddr, u8CECDstAddr;
uint8_t u8CECErrorFlag, u8CECReAckFlag;
uint16_t u16CECAckFlag, u16CECAckCheckFlag;
//u16CECAckFlag define
//#define HeadAck 0x8000
//#define Data1Ack 0x4000
//#define Data2Ack 0x2000
//#define Data3Ack 0x1000
//......
//#define Data15Ack 0x0001
/**
* @brief This function do all CEC protocal stack
* @param[in] none
* @return none
* @note this functun is called from Timer3 interrupt
*
*/
void CEC_TimerEvent()
{
uint8_t u8Temp;
if (u8CECTRStatus == CECtransmit) // CEC in trasmit process
{
switch (u8CECState)
{
case 0: //check bus free time for 5bit data period
if (P33 == 1)
{
CEC_SetTimer(500);
u8CECTimerCNT++;
if (u8CECTimerCNT > 24)
{
CEC_SetTimer(3700);
u8CECState = 1;
u8CECTimerCNT = 0;
P33 = 0; //CECpin low, start bit
}
}
else
{
CECSetReceive();
CEC_SetTimer(5000); //check if data need retransmit
u8CECTimerCNT = 0;
//printf("send fail, turn to receiver \n");
}
break;
case 1: //send head and check arbitration
{
if (u8CECTimerCNT == 0)
{
P33 = 1; //start bit high level
for (u8Temp = 20; u8Temp >0; u8Temp--);
if (P33 == 0) //start bit arbitration lost
{
//printf("start bit arbitration lost \n");
CECSetReceive(); //turn to receive mode
CEC_SetTimer(5000); //check if data need retransmit
return;
}
else
{
CEC_SetTimer(800); //start bit ,high level 0.8 ms
u8CECTimerCNT++;
u8CECSubCNT = 0;
}
}
else if (u8CECTimerCNT < 9) //send head data
{
if (u8CECSubCNT == 0)
{
P33 = 0; //data bit start
if (au8CECTxData[0] & (0x80 >> (u8CECTimerCNT - 1)))
CEC_SetTimer(600);//bit 1, low level 0.6ms
else
CEC_SetTimer(1500);//bit 0, low level 1.5ms
u8CECSubCNT = 1;
//printf("head bit :%d \n",u8CECTimerCNT);
}
else
{
P33 = 1; //CECpin high
for (u8Temp = 30; u8Temp >0; u8Temp--);
if (P33 == 0) //arbitration lost
{
printf("head bit :%d arbitration lost \n", u8CECTimerCNT);
//u8CECTxLength=0; //arbitration lost ,stop tran
CECSetReceive(); //turn to receive mode
CEC_SetTimer(15000);//must wait 15ms then retrans, check if data need retransmit
return;
}
else
{
if (au8CECTxData[0] & (0x80 >> (u8CECTimerCNT - 1)))
CEC_SetTimer(1790);//bit 1, high level 1.8 ms
else
CEC_SetTimer(890);//bit 0, high level 0.9 ms
u8CECTimerCNT++;
u8CECSubCNT = 0;
}
}
}
else if (u8CECTimerCNT == 9) //EOM bit
{
if (u8CECSubCNT == 0) //EOM bit low level
{
P33 = 0; //EOM bit start
if (u8CECTxRemain > 1)
CEC_SetTimer(1500);//bit 0, low level 1.5ms
else
CEC_SetTimer(600);//bit 1, low level 0.6ms
u8CECSubCNT = 1;
}
else
{
P33 = 1; //EOM bit start
if (u8CECTxRemain > 1)
CEC_SetTimer(900);//bit 0, high level 0.9ms
else
CEC_SetTimer(1800);//bit 1, high level 1.8ms
u8CECSubCNT = 0;
u8CECTimerCNT++;
}
}
else if (u8CECTimerCNT == 10) //ACK bit
{
if (u8CECSubCNT == 0)
{
CEC_SetTimer(600);
P33 = 0; //ACK bit low level
u8CECSubCNT = 1;
}
else if (u8CECSubCNT == 1)
{
CEC_SetTimer(400);
P33 = 1; //ACK bit high level
u8CECSubCNT = 2;
}
else if (u8CECSubCNT == 2) //check ack bit at 1ms positon
{
CEC_SetTimer(1400);
if (P33 == 0) //get ack bit
{
u16CECAckFlag |= 0x8000;
}
u8CECSubCNT = 0;
u8CECTimerCNT = 0;
u8CECState = 2;
u8CECTxRemain--;
}
}
}
break;
case 2: //transmit all data
{
if (u8CECTxRemain)
{
if (u8CECTimerCNT < 8)
{
if (u8CECSubCNT == 0)
{
P33 = 0; //data bit start
if (au8CECTxData[u8CECTxLength - u8CECTxRemain] & (0x80 >> u8CECTimerCNT))
CEC_SetTimer(600);//bit 1, low level 0.6ms
else
CEC_SetTimer(1500);//bit 0, low level 1.5ms
u8CECSubCNT = 1;
}
else
{
P33 = 1; //CECpin high
if (au8CECTxData[u8CECTxLength - u8CECTxRemain] & (0x80 >> u8CECTimerCNT))
CEC_SetTimer(1800);//bit 1, high level 1.8 ms
else
CEC_SetTimer(900);//bit 0, high level 0.9 ms
u8CECTimerCNT++;
u8CECSubCNT = 0;
}
}
else if (u8CECTimerCNT == 8) //EOM bit
{
if (u8CECSubCNT == 0) //EOM bit low level
{
P33 = 0; //EOM bit start
if (u8CECTxRemain > 1)
CEC_SetTimer(1500);//bit 0, low level 1.5ms
else
CEC_SetTimer(600);//bit 1, low level 0.6ms
u8CECSubCNT = 1;
}
else
{
P33 = 1; //EOM bit start
if (u8CECTxRemain > 1)
CEC_SetTimer(900);//bit 0, high level 0.9ms
else
CEC_SetTimer(1800);//bit 1, high level 1.8ms
u8CECSubCNT = 0;
u8CECTimerCNT++;
}
}
else if (u8CECTimerCNT == 9) //ACK bit
{
if (u8CECSubCNT == 0)
{
CEC_SetTimer(600);
P33 = 0; //ACK bit low level
u8CECSubCNT = 1;
}
else if (u8CECSubCNT == 1)
{
CEC_SetTimer(400);
P33 = 1; //ACK bit high level
u8CECSubCNT = 2;
}
else if (u8CECSubCNT == 2) //check ack bit at 1ms positon
{
CEC_SetTimer(1400);
if (P33 == 0) //get ack bit
{
u16CECAckFlag |= 0x8000 >> (u8CECTxLength - u8CECTxRemain);
}
u8CECSubCNT = 0;
u8CECTimerCNT = 0;
u8CECTxRemain--;
}
}
}
else //all data transmit out
{
if (u16CECAckFlag != u16CECAckCheckFlag) //check ack, if not match, retransmit data
{
if (((au8CECTxData[0] >> 4) & 0x0f) == (au8CECTxData[0] & 0x0f))
{
//check general call, no need retransmit
u8CECTxLength = u8CECRetranCNT = 0;
CECSetReceive(); //turn to receive mode
return;
}
else
{
u8CECRetranCNT++;
//printf("retrans :%d times \n", u8CECRetranCNT);
if (u8CECRetranCNT > 4) //retrans fail 5 times, abort transmit!
{
u8CECTxLength = u8CECRetranCNT = 0;
CECSetReceive(); //turn to receive mode
return;
}
SendData(u8CECTxLength);
//CECSetReceive();
//CEC_SetTimer(3000); //check if data need retransmit
}
}
else
{
u8CECTxLength = u8CECRetranCNT = 0;
CECSetReceive(); //turn to receive mode
return;
}
}
}
break;
}
}
else if (u8CECTRStatus == CECReceive)
{
if (u8CECEIntStatus == 0)
{
CECSetReceive(); //timer is not trigged by ext int, it's means last trigger is a noise
CEC_SetTimer(5000); //check if data need transmit
//printf("Noise trigger, checked 1\n");
return;
}
switch (u8CECState)
{
case 0: //receive check start bit
{
if (u8CECTimerCNT < 5)
{
if (P33 == 1)
{
//it's noise,not start bit
//printf("Noise trigger, checked2: %d \n",u8CECTimerCNT);
CECSetReceive();
return;
}
else
{
u8CECTimerCNT++;
if (u8CECTimerCNT == 5)
CEC_SetTimer(1100);
else
CEC_SetTimer(500);
}
}
else if (u8CECTimerCNT == 5)
{
if (P33 == 0)
{
//it's noise,not start bit
//printf("Noise trigger, checked 3\n");
CECSetReceive();
return;
}
else
{
u8CECTimerCNT = u8CECRxLength = u8CECErrorFlag = 0;
u8CECState = 1;
NVIC_EnableIRQ(CECExtInt); //enable ext int check
u8CECEIntStatus = 0; //check if the timer is trigged from ext int
CEC_SetTimer(1000000); //for safe,
//printf("Get start bit\n");
}
}
}
break;
case 1:
{
if (u8CECErrorFlag == 0xff)
{ //user can add error process here:
/* u8CECState=2;
u8CECErrorFlag=u8CECTimerCNT=u8CECSubCNT=u8CECRxLength=0;
//NVIC_DisableIRQ(CECExtInt); //enable ext int check
GPIO_SetMode(CECPort, CECPin, GPIO_PMD_OPEN_DRAIN);
P33=0; //CECpin low, for ACK bit
CEC_SetTimer(3600); //force low time 3.4~3.8ms, ack bit timming error
//printf("bit timing error \n");
*/
}
if (u8CECTimerCNT < 8)
{
if (P33 == 1)
au8CECRxData[u8CECRxLength] |= 0x80 >> u8CECTimerCNT; //get high CEC bit
else
au8CECRxData[u8CECRxLength] &= ~(0x80 >> u8CECTimerCNT); //get low CEC bit
u8CECTimerCNT++;
NVIC_EnableIRQ(CECExtInt); //enable ext int check
u8CECEIntStatus = 0; //check if the timer is trigged from ext int
CEC_SetTimer(2000);
}
else if (u8CECTimerCNT == 8) //EOM proc
{
if (P33 == 1) //check end of message
u8CECEOM = 1;
else
u8CECEOM = 0;
if (u8CECLogicalAddr == (au8CECRxData[0] & 0xf)) //check dest address, if equal, receiver will reply ACK
u8CECReAckFlag = u8CECSubCNT = 1; //flag for ACK
else
u8CECReAckFlag = u8CECSubCNT = 0; //flag for NOACK
u8CECTimerCNT = 9;
NVIC_EnableIRQ(CECExtInt); //enable ext int check
u8CECEIntStatus = 0; //check if the timer is trigged from ext int
CEC_SetTimer(1000000);
//printf("received:%d data:%d \n",u8CECRxLength,au8CECRxData[u8CECRxLength]);
u8CECRxLength++;
}
else if (u8CECTimerCNT == 9) //ACK process
{
if (u8CECSubCNT == 0)
{
if (u8CECEOM == 1)
{
u8CECGetData = 1; //receive finished
if (u8CECTxLength)
{
SendData(u8CECTxLength); //goto transmit mode
CEC_SetTimer(10000); //delay for safe, long delay for ACK bit finish
}
else
CECSetReceive(); //trun receive state
//printf("Get data 1 \n");
}
else
{
NVIC_EnableIRQ(CECExtInt); //enable ext int check
u8CECEIntStatus = 0; //check if the timer is trigged from ext int
CEC_SetTimer(1000000);
u8CECTimerCNT = u8CECSubCNT = 0; //keep receive other data
}
}
else if (u8CECSubCNT == 1)
{
GPIO_SetMode(CECPort, CECPin, GPIO_PMD_OPEN_DRAIN);
P33 = 0; //CECpin low, for ACK bit
CEC_SetTimer(1400); //ack low 1.5ms, after TCL test, this need set 1300
u8CECEIntStatus = 0xff;
u8CECSubCNT = 2;
}
else if (u8CECSubCNT == 2)
{
P33 = 1; //CECpin high, for ACK bit
CEC_SetTimer(300); //should be 900,but it will mask next falling edge, so set 300
u8CECEIntStatus = 0xff;
u8CECSubCNT = 3;
}
else if (u8CECSubCNT == 3)
{
//followed 2 line clear ACK high caused Ext pending int, must do!
ClearExtInt();
NVIC_ClearPendingIRQ(CECExtInt);
GPIO_SetMode(CECPort, CECPin, GPIO_PMD_INPUT);
if (u8CECEOM == 1)
{
u8CECGetData = 1; //receive finished
if (u8CECTxLength)
{
SendData(u8CECTxLength); //goto transmit mode
CEC_SetTimer(7000); //delay for safe
}
else
CECSetReceive();
//printf("Get data 2 \n");
}
else
{
NVIC_EnableIRQ(CECExtInt); //enable ext int check
u8CECEIntStatus = 0; //check if the timer is trigged from ext int
CEC_SetTimer(1000000);
u8CECTimerCNT = u8CECSubCNT = 0; //keep receive other data
//printf("keep receive \n");
}
}
}
}
break;
case 2:
{
/* printf("low level keeped 3.6ms \n");
ClearExtInt();
NVIC_ClearPendingIRQ(CECExtInt);
GPIO_SetMode(CECPort, CECPin, GPIO_PMD_INPUT);
if(u8CECTxLength)
{
SendData(u8CECTxLength); //go to transmit mode
CEC_SetTimer(10000); //delay for safe, long delay for ACK bit finish
}
else
CECSetReceive(); //go to receive state
*/
}
default:
break;
}
}
else if (u8CECTRStatus == CECIdle) // if transmit failed, it will goto receive mode, if there is not any siginal for a certain time,it will goto transmit mode again
{
if (u8CECTxLength > 0)
SendData(u8CECTxLength); //goto transmit mode
else
CECSetReceive(); //go to receive state
}
return;
}