// Write address void LED_writeReg( uint8_t bus, uint8_t addr, uint8_t reg, uint8_t val, uint8_t page ) { /* info_msg("I2C Write bus("); printHex( bus ); print(")addr("); printHex( addr ); print(")reg("); printHex( reg ); print(")val("); printHex( val ); print(")page("); printHex( page ); print(")" NL); */ // Reg Write Setup uint16_t writeData[] = { addr, reg, val }; // Setup page LED_setupPage( bus, addr, page ); // Write register while ( i2c_send( bus, writeData, sizeof( writeData ) / 2 ) == -1 ) delay_us( ISSI_SendDelay ); // Delay until written while ( i2c_busy( bus ) ) delay_us( ISSI_SendDelay ); }
// Write register on all ISSI chips // Prepare pages first, then attempt write register with a minimal delay between chips void LED_syncReg( uint8_t reg, uint8_t val, uint8_t page ) { // Setup each of the pages for ( uint8_t ch = 0; ch < ISSI_Chips_define; ch++ ) { LED_setupPage( LED_ChannelMapping[ ch ].bus, LED_ChannelMapping[ ch ].addr, page ); } // Reg Write Setup uint16_t writeData[] = { 0, reg, val }; // Write to all the registers for ( uint8_t ch = 0; ch < ISSI_Chips_define; ch++ ) { writeData[0] = LED_ChannelMapping[ ch ].addr; uint8_t bus = LED_ChannelMapping[ ch ].bus; // Delay very little to help with synchronization while ( i2c_send( bus, writeData, sizeof( writeData ) / 2 ) == -1 ) delay_us(10); } // Delay until written while ( i2c_any_busy() ) delay_us( ISSI_SendDelay ); }
// Write ISSI page void LED_sendPage( uint8_t bus, uint8_t addr, uint16_t *buffer, uint32_t len, uint8_t page ) { /* info_msg("I2C Send Page: bus("); printHex( bus ); print(")addr("); printHex( addr ); print(")len("); printHex( len ); print(")page("); printHex( page ); print(")data[]("); for ( uint8_t c = 0; c < 9; c++ ) { printHex( buffer[c] ); print(" "); } print("..)" NL); */ // Page Setup LED_setupPage( bus, addr, page ); // Write page to I2C Tx Buffer while ( i2c_send( bus, buffer, len ) == -1 ) delay_us( ISSI_SendDelay ); }
// Zero out given ISSI page void LED_zeroPages( uint8_t bus, uint8_t addr, uint8_t startPage, uint8_t numPages, uint8_t startReg, uint8_t endReg ) { // Clear Page // Max length of a page + chip id + reg start uint16_t clearPage[2 + ISSI_PageLength] = { 0 }; clearPage[0] = addr; clearPage[1] = startReg; // Iterate through given pages, zero'ing out the given register regions for ( uint8_t page = startPage; page < startPage + numPages; page++ ) { // Page Setup LED_setupPage( bus, addr, page ); // Zero out page while ( i2c_send( bus, clearPage, 2 + endReg - startReg ) == -1 ) delay_us( ISSI_SendDelay ); } // Wait until finished zero'ing while ( i2c_busy( bus ) ) delay_us( ISSI_SendDelay ); }
// Read address uint8_t LED_readReg( uint8_t bus, uint8_t addr, uint8_t reg, uint8_t page ) { /* info_msg("I2C Read Bus: "); printHex( bus ); print(" Addr: "); printHex( addr ); print(" Reg: "); printHex( reg ); print(" Page: "); printHex( page ); print( NL ); */ #if ISSI_Chip_31FL3731_define == 1 || ISSI_Chip_31FL3732_define == 1 // Software shutdown must be enabled to read registers LED_writeReg( bus, addr, 0x0A, 0x00, ISSI_ConfigPage ); #endif // Setup page LED_setupPage( bus, addr, page ); // Register Read Command uint16_t regReadCmd[] = { addr, reg, I2C_RESTART, addr | 0x1, I2C_READ }; uint8_t recv_data; // Request single register byte while ( i2c_read( bus, regReadCmd, sizeof( regReadCmd ) / 2, &recv_data ) == -1 ) delay_us( ISSI_SendDelay ); #if ISSI_Chip_31FL3731_define == 1 || ISSI_Chip_31FL3732_define == 1 // Disable software shutdown LED_writeReg( bus, addr, 0x0A, 0x01, ISSI_ConfigPage ); #endif return recv_data; }
inline void LED_scan() { // Latency measurement start Latency_start_time( ledLatencyResource ); // Check for current change event if ( LED_currentEvent ) { // Turn LEDs off in low power mode if ( LED_currentEvent < 150 ) { LED_enable_current = 0; // Pause animations and clear display Pixel_setAnimationControl( AnimationControl_WipePause ); } else { LED_enable_current = 1; // Start animations Pixel_setAnimationControl( AnimationControl_Forward ); } LED_currentEvent = 0; } // Check if an LED_pause is set // Some ISSI operations need a clear buffer, but still have the chip running if ( LED_pause ) goto led_finish_scan; // Check enable state if ( LED_enable && LED_enable_current ) { // Disable Hardware shutdown of ISSI chips (pull high) GPIO_Ctrl( hardware_shutdown_pin, GPIO_Type_DriveHigh, GPIO_Config_Pullup ); } // Only write pages to I2C if chip is enabled (i.e. Hardware shutdown is disabled) else { // Enable hardware shutdown GPIO_Ctrl( hardware_shutdown_pin, GPIO_Type_DriveLow, GPIO_Config_Pullup ); goto led_finish_scan; } // Check if any I2C buses have errored // Reset the buses and restart the Frame State if ( i2c_error() ) { i2c_reset(); Pixel_FrameState = FrameState_Update; } // Only start if we haven't already // And if we've finished updating the buffers if ( Pixel_FrameState == FrameState_Sending ) goto led_finish_scan; // Only send frame to ISSI chip if buffers are ready if ( Pixel_FrameState != FrameState_Ready ) goto led_finish_scan; // Adjust frame rate (i.e. delay and do something else for a bit) Time duration = Time_duration( LED_timePrev ); if ( duration.ms < LED_framerate ) goto led_finish_scan; // FPS Display if ( LED_displayFPS ) { // Show frame calculation dbug_msg("1frame/"); printInt32( Time_ms( duration ) ); print("ms + "); printInt32( duration.ticks ); print(" ticks"); // Check if we're not meeting frame rate if ( duration.ms > LED_framerate ) { print(" - Could not meet framerate: "); printInt32( LED_framerate ); } print( NL ); } // Emulated brightness control // Lower brightness by LED_brightness #if ISSI_Chip_31FL3731_define == 1 for ( uint8_t chip = 0; chip < ISSI_Chips_define; chip++ ) { for ( uint8_t ch = 0; ch < LED_EnableBufferLength; ch++ ) { LED_pageBuffer_brightness[ chip ].ledctrl[ ch ] = LED_pageBuffer[ chip ].ledctrl[ ch ]; } for ( uint8_t ch = 0; ch < LED_BufferLength; ch++ ) { // Don't modify is 0 if ( LED_pageBuffer[ chip ].buffer[ ch ] == 0 || LED_brightness == 0 ) { LED_pageBuffer_brightness[ chip ].buffer[ ch ] = 0; continue; } // XXX (HaaTa) Yes, this is a bit slow, but it's pretty accurate LED_pageBuffer_brightness[ chip ].buffer[ ch ] = (LED_pageBuffer[ chip ].buffer[ ch ] * LED_brightness) / 0xFF; } } #endif // Update frame start time LED_timePrev = Time_now(); // Set the page of all the ISSI chips // This way we can easily link the buffers to send the brightnesses in the background for ( uint8_t ch = 0; ch < ISSI_Chips_define; ch++ ) { uint8_t bus = LED_ChannelMapping[ ch ].bus; // Page Setup LED_setupPage( bus, LED_ChannelMapping[ ch ].addr, ISSI_LEDPwmPage ); } // Send current set of buffers // Uses interrupts to send to all the ISSI chips // Pixel_FrameState will be updated when complete LED_chipSend = 0; // Start with chip 0 LED_linkedSend(); led_finish_scan: // Latency measurement end Latency_end_time( ledLatencyResource ); }
inline void LED_scan() { // Latency measurement start Latency_start_time( ledLatencyResource ); // Check for current change event if ( LED_currentEvent ) { // Turn LEDs off in low power mode if ( LED_currentEvent < 150 ) { LED_enable = 0; // Pause animations and clear display Pixel_setAnimationControl( AnimationControl_WipePause ); } else { LED_enable = 1; // Start animations Pixel_setAnimationControl( AnimationControl_Forward ); } LED_currentEvent = 0; } // Check if an LED_pause is set // Some ISSI operations need a clear buffer, but still have the chip running if ( LED_pause ) goto led_finish_scan; // Check enable state if ( LED_enable ) { // Disable Hardware shutdown of ISSI chips (pull high) GPIOB_PSOR |= (1<<16); } // Only write pages to I2C if chip is enabled (i.e. Hardware shutdown is disabled) else { // Enable hardware shutdown GPIOB_PCOR |= (1<<16); goto led_finish_scan; } // Only start if we haven't already // And if we've finished updating the buffers if ( Pixel_FrameState == FrameState_Sending ) goto led_finish_scan; // Only send frame to ISSI chip if buffers are ready if ( Pixel_FrameState != FrameState_Ready ) goto led_finish_scan; // Adjust frame rate (i.e. delay and do something else for a bit) Time duration = Time_duration( LED_timePrev ); if ( duration.ms < LED_framerate ) goto led_finish_scan; // FPS Display if ( LED_displayFPS ) { // Show frame calculation dbug_msg("1frame/"); printInt32( Time_ms( duration ) ); print("ms + "); printInt32( duration.ticks ); print(" ticks"); // Check if we're not meeting frame rate if ( duration.ms > LED_framerate ) { print(" - Could not meet framerate: "); printInt32( LED_framerate ); } print( NL ); } // Emulated brightness control // Lower brightness by LED_brightness #if ISSI_Chip_31FL3731_define == 1 uint8_t inverse_brightness = 0xFF - LED_brightness; for ( uint8_t chip = 0; chip < ISSI_Chips_define; chip++ ) { for ( uint8_t ch = 0; ch < LED_BufferLength; ch++ ) { // Don't modify is 0 if ( LED_pageBuffer[ chip ].buffer[ ch ] == 0 ) { LED_pageBuffer_brightness[ chip ].buffer[ ch ] = 0; continue; } LED_pageBuffer_brightness[ chip ].buffer[ ch ] = LED_pageBuffer[ chip ].buffer[ ch ] - inverse_brightness < 0 ? 0x0 : LED_pageBuffer[ chip ].buffer[ ch ] - inverse_brightness; } } #endif // Update frame start time LED_timePrev = Time_now(); // Set the page of all the ISSI chips // This way we can easily link the buffers to send the brightnesses in the background for ( uint8_t ch = 0; ch < ISSI_Chips_define; ch++ ) { uint8_t bus = LED_ChannelMapping[ ch ].bus; // Page Setup LED_setupPage( bus, LED_ChannelMapping[ ch ].addr, ISSI_LEDPwmPage ); #if ISSI_Chip_31FL3731_define == 1 || ISSI_Chip_31FL3732_define == 1 // Reset LED enable mask // XXX At high speeds, the IS31FL3732 seems to have random bit flips // To get around this, just re-set the enable mask before each send // XXX Might be sufficient to do this every N frames though while ( i2c_send( bus, (uint16_t*)&LED_ledEnableMask[ ch ], sizeof( LED_EnableBuffer ) / 2 ) == -1 ) delay_us( ISSI_SendDelay ); #endif } // Send current set of buffers // Uses interrupts to send to all the ISSI chips // Pixel_FrameState will be updated when complete LED_chipSend = 0; // Start with chip 0 LED_linkedSend(); led_finish_scan: // Latency measurement end Latency_end_time( ledLatencyResource ); }