From initialisation or startup the CISC CPU is responsible for setting up the RISC engine into RAM, collect all the saved data required to generate the selected dataset, then having placed the "compiled" dataset in memory trigger the RISC engine to generate the RDS signal. From then on the CISC CPU has nothing more to do except wait!
Well what does the CISC CPU wait for? Answer: a change in RDS data, the reception of UECP data is what we are going to look into here. First the UECP stream has to be collected:
The above code is a function called by the serial IRQ handler after handling reception, error detection etc of the serial port bytes into an intermadiate buffer size 32 bytes. The byte is recovered from this buffer with the function RecvChar(&in), with the new byte in &in and the function return with a possible error value. So there is alot of low level handler that depends upon the specific MCU used.
InCounter = 1 is a flag that kills or re-starts the inter-byte timer. bHandleData is a flag that indicates completion of frame reception and triggers de-stuffing and CRC checking of the just received frame.
Once any de-stuffing, CRC based error correction has been carried out we are ready to decode the UECP frame. To this end a switch can be used based upon the command byte in the frame but is this frame really for this encoder. The first two bytes of the UECP_buffer are the site address and encoder address.
// There are k chars in the UECP buffer now i = ((word)UECP_InBuffer[1] << 8) | (word)UECP_InBuffer[2]; // Spilt into site address and encoder address site = ((i & 0xFFC0) >> 6); encoder = (byte)(i & 0x3F); // The appropriate own values are site_address, encoder_address // The global addressing needs handling as well bGo = FALSE; if((site == site_address) && (encoder == encoder_address)) bGo = TRUE; else if((site_address == 0x00) && (encoder == encoder_address)) bGo = TRUE; else if((site == site_address) && (encoder == 0x00)) bGo = TRUE; else if((site == 0x00) && (encoder == 0x00)) bGo = TRUE;
This is the check of the incoming and device addresses, if you want to use use a single "if", feel free...
// Next come: // SQC the sequence number // MFL the message field length // MSG the message // CRC the CCITT crc x16 + x12 + x5 + 1 // The sequence number is need for a possible reply sequence_number = UECD_InBuffer[3]; mfl = UECD_InBuffer[4]; // Extract the message. for(i = 0; i < mfl; i++) message[i] = UECD_InBuffer[(i + 5)];
Now that "message" has the command, we extract MEC (mec) and run the message through a switch. Here, as example only, two different mec's are interpreted.
switch (mec) { case MEC_PI: // MEC 01 // DSN 00..FF // PSN 00..FF // MED 00..FF PI (MSB) // MED 00..FF PI (LSB) // The PSN is ignored _PI.bytes.upper = message[3]; _PI.bytes.lower = message[4]; // Store new PI Set_PI(_PI); // Re-construct the data set (void)Re_ConstructDataSet((byte)current_data_set_number); bAck = TRUE; last_mec_length = 5; break; case MEC_DI_PTYI: // MEC 04 // DSN 00..FF // PSN 00..FF // MED 00..0F // Bit 0: mono/stereo // Bit 1: artificial head no/yes // Bit 2: compressed no/yes // Bit 3: dynamic PTY Indicator _DI.di = (byte)(message[3] & 0x0F); // Store new DI Set_DI(_DI); // Re-construct the data set (void)Re_ConstructDataSet((byte)current_data_set_number); bAck = TRUE; last_mec_length = 4; break; }
Here the PI and DI_PTYI commands are extracted from the message. The Set_XX() function stores the new data into the flash memory. Since this is a fundamental change the dataset is re-constructed (the current working data set will have been set by a previous UECP command and does not have to be the dataset in active transmission)
bAck flags that an UECP acknowledge is required, informing of the last MEC length. _DI is a structure that contains the DI information. Some commands are requests for information, so there is a informative reply rather than an ACK.
Manufacture specific commands can be handled in the same way!