void ResetToInitState(void) { State = STATE_RESET; InterruptPulse = 0; DrivingNeighborBus = 0; ticks = 0; DisableDownstreamInterrupt; // Disable all GPIO interrupts on port 1 Setup_As_Input(PIN_UPSTREAM); Setup_As_Output(PIN_DOWNSTREAM); // Reset all downstream modules PRT1DR = 0; // Neighbor bus drives low Disable_I2C_Pullups; DL_Reg.ID = 0; DL_Reg.Config = DL_REGS_CONFIG_DISABLE; EzI2Cs_SetAddr(0); }
// Returns 1 if DaisyLink initialization is complete and Module // function should be active. Returns 0 if Module function should // ignore I2C activity. For some reason this compiler does not // allow a function to return a bool. char DaisyLink(BYTE *I2CRAM) { struct DL_Regs *vDL_Regs = (struct DL_Regs *)I2CRAM; // The copy of DL_Reg "seen" by the EzI2C char active = 0; switch( State ) { // In this state, a reset condition exists. The I2C should be unresponsive // and the DaisyLink state should wait until the upstream line goes high // before proceeding to the SETUP state. case STATE_RESET: if( IsNotActive(PIN_UPSTREAM) ) // If the mainboard has released the RESET condition { State = STATE_SETUP; // Move to the Setup state DL_Reg.ID = DEFAULT_I2C_ID; Refresh(I2CRAM); // Update I2C registers to show default I2C ID Enable_I2C_Pullups; EzI2Cs_SetAddr(DEFAULT_I2C_ID); // Make this module respond to the I2C default ID } break; // In this state, the Module will respond to DaisyLink registers at the I2C default address. // This state is maintained until either the upstream line goes low again, or the I2C // address is changed. case STATE_SETUP: if( IsActive(PIN_UPSTREAM) ) // If the module is RESET again { ResetToInitState(); Refresh(I2CRAM); } else if( DEFAULT_I2C_ID != vDL_Regs->ID ) // If the I2C ID has been set, move to the next state { DL_Reg.ID = vDL_Regs->ID; // Fetch the newly set I2C ID State = STATE_STANDBY; // Move to the Standby state EzI2Cs_SetAddr(DL_Reg.ID); // Make this module respond to the newly set I2C ID Setup_As_Input(PIN_DOWNSTREAM); // Allow the downstream module to leave the RESET state } break; // In this state, the module will respond to DaisyLink registers at the assigned I2C address. // This state is maintained until either the upstream line goes low again, or the DL_REGS_CONFIG_DISABLE // bit is set low by the mainboard (and with it, the state of the DL_REGS_CONFIG_PULLUPS bit). case STATE_STANDBY: if( IsActive(PIN_UPSTREAM) ) // If the module is RESET again { ResetToInitState(); Refresh(I2CRAM); } else if( 0 == (vDL_Regs->Config & DL_REGS_CONFIG_DISABLE)) // If the mainboard has finished setting up this module { DL_Reg.Config = (vDL_Regs->Config & DL_REGS_CONFIG_PULLUPS); // Keep the new resistor setting if( 0 == (DL_Reg.Config & DL_REGS_CONFIG_PULLUPS) ) // If pullups are disabled (this is not the last module in the chain) { Disable_I2C_Pullups; } Refresh(I2CRAM); active = 1; // Enable the functionality of this module State = STATE_ACTIVE; // Move to the Active state } break; // In this state, the module will respond to DaisyLink Registers and all module function registers // at the assigned I2C address. It will remain in this state until interrupted by the module's // main function, a module upstream, or the mainboard (a reset). case STATE_ACTIVE: // If we are in an interrupt state if( DL_Reg.Config & DL_REGS_CONFIG_INTERRUPT ) { // If the config register has been cleared if( !(vDL_Regs->Config & DL_REGS_CONFIG_INTERRUPT) ) { DL_Reg.Config &= ~DL_REGS_CONFIG_INTERRUPT; // Clear this module's interrupt state vDL_Regs->Config = DL_Reg.Config; InterruptPulse = 0; // Indicate that we can stop pulling on the upstream neighbor bus } vDL_Regs->ID = DL_Reg.ID; // Refresh the ID register (read from I2C bus) // Refresh all DaisyLink registers except for the ID and Config registers memcpy(&(vDL_Regs->DLVersion), &(DL_Reg.DLVersion), sizeof(struct DL_Regs)-2); } else { Refresh(I2CRAM); // Refresh the DaisyLink I2C registers } // If this module's interrupt pulse has reached its maximum length if( InterruptPulse && (((WORD)ticks - intStart) > 10) ) { InterruptPulse = 0; // Indicate that we can stop pulling on the upstream neighbor bus } // NOTE: Only sample the downstream neighbor bus once while interrupts are off and act on what you find // before restoring interrupts. The interrupt routine will then handle any changes that might have // occurred while interrupts were off once interrupts are enabled again. DisableDownstreamInterrupt; // Disable interrupts from the downstream neighbor bus if( IsActive(PIN_DOWNSTREAM) ) { // If we should be driving the upstream bus, but aren't if( !DrivingNeighborBus ) { Setup_As_Output(PIN_UPSTREAM); DrivingNeighborBus = 1; // Drive the upstream neighbor bus line } } else { // If we shouldn't be driving the upstream bus, but are if( DrivingNeighborBus && !InterruptPulse ) { Setup_As_Input(PIN_UPSTREAM); DrivingNeighborBus = 0; // Release the upstream neighbor bus line } } // If upstream neighbor bus is being driven and it isn't by us if( !DrivingNeighborBus && IsActive(PIN_UPSTREAM) ) { // Reset the DaisyLink State = STATE_RESET; ResetToInitState(); Refresh(I2CRAM); break; } EnableDownstreamInterrupt; // Enable the downstream neighbor bus interrupt active = 1; break; default: ResetToInitState(); Refresh(I2CRAM); break; } return active; }
void I2C_Init(void) { EzI2Cs_SetRamBuffer(sizeof(I2C_Regs), 0, (char*)(&I2C_Regs)); EzI2Cs_SetAddr(I2C_SLAVE_ADDRESS); EzI2Cs_Start(); // Turn on I2C }