#include "../memories/memories.hh" /* * SpiBus.cpp * * Created on: 6 июн. 2019 г. * Author: krugliy */ #include "SpiBus.hh" #include #include peripheral::ISerialPort* driver::SpiBus::SpiBusMaster::transfer_request( SpiAbonent* spi_abonent) { if( active_abonent && serial_port.isDataReady() ) { active_abonent->transmite_complete_handler( serial_port ); cancel_request( active_abonent ); } // ITransferHandler * volatile prev_handler = active_handler; SpiAbonent * volatile prev_abonenet = active_abonent; if( !prev_abonenet && !collision.isTiedTo( spi_abonent ) ) { while( collision.popLink( static_cast( spi_abonent ) ) ); // spi.reset(); // interrupt_ctrl.enable(); if( active_abon_setting != spi_abonent->getSetting() ) { serial_configurator.applySetting( spi_abonent->getSetting().baud_rate, spi_abonent->getSetting().clk_scheme, spi_abonent->getSetting().transfer_delay, spi_abonent->getSetting().loop_back_enable ); active_abon_setting = spi_abonent->getSetting(); } active_abonent = spi_abonent; return & serial_port; } else if( static_cast( prev_abonenet ) != spi_abonent && prev_abonenet ) { collision.putLink( static_cast( spi_abonent ), prev_abonenet ); } return nullptr; } void driver::SpiBus::SpiBusMaster::cancel_request(SpiAbonent* spi_abonent) { while( collision.popLink( spi_abonent ) ); if( active_abonent == spi_abonent ) { // interrupt_ctrl.disable(); // spi.reset(); active_abonent = nullptr; } } driver::SpiBus::SpiBusMaster::SpiBusMaster( peripheral::ISerialPort& _serial_port, peripheral::ISerialPortConfigurator& _serial_configurator) : serial_port(_serial_port), active_abonent( nullptr ), serial_configurator(_serial_configurator), active_abon_setting(), collision() {} peripheral::ISerialPort * driver::SpiBus::createAbonent( peripheral::IGpio& chip_select, const SpiBusAbonentConfig& abon_config, std::pmr::memory_resource* allocator ) { if( num_spi_abonent < max_spi_bus_abonents ) { num_spi_abonent++; //todo: использовать алокатор return memories::instance_object( *allocator, spiBusMaster, chip_select, abon_config ); } return nullptr; } driver::SpiBus::SpiBus(peripheral::ISerialPort& _serial_port, peripheral::ISerialPortConfigurator& _serial_configurator, std::size_t _max_spi_abonents ) : max_spi_bus_abonents( _max_spi_abonents ), num_spi_abonent( 0 ), spiBusMaster( _serial_port, _serial_configurator ) {} bool driver::SpiBus::SpiAbonent::transmite(const void* bit_stream, int bit_count) { if( peripheral::ISerialPort * port = serial_bus.transfer_request( this ) ) { num_words = bit_count / CHAR_BIT; if( bit_count % CHAR_BIT ) num_words += 1; chip_select.on(); if( not abon_config.getAbonentSetting().tx_inv ) { if( not port->transmite( bit_stream, bit_count ) ) { chip_select.off(); } else return true; } else { char tx_buff[fifo_buff_size]; for( int i = 0; i < num_words; i++ ) { tx_buff[i] = ~*static_cast(bit_stream); bit_stream = static_cast(bit_stream) + 1; } if( not port->transmite( tx_buff, bit_count ) ) { chip_select.off(); } else return true; } } return false; } bool driver::SpiBus::SpiAbonent::receive(void* data) { if( isDataReady() ) { if( abon_config.getAbonentSetting().rx_inv ) { for( int i = 0; i < num_words; i++ ) static_cast(data)[i] = ~rx_buff[i]; } else { for( int i = 0; i < num_words; i++ ) static_cast(data)[i] = rx_buff[i]; } serial_bus.cancel_request( this ); data_ready = false; return true; } return false; } driver::SpiBus::SpiAbonent::SpiAbonent(SpiBusMaster& _bus_master, peripheral::IGpio& _chip_select, const SpiBusAbonentConfig& _abon_config) : serial_bus(_bus_master), abon_config(_abon_config), chip_select(_chip_select, _abon_config.getCsActiveLevel()), rx_buff(), num_words(0) {} bool driver::SpiBus::SpiAbonent::isDataReady() { return serial_bus.is_response_ready( this ) || data_ready; } driver::SpiBus::SpiAbonent::~SpiAbonent() { //надо сказать мастеру, что мы удаляемся - приемные буфера больше не действительны } void driver::SpiBus::SpiAbonent::transmite_complete_handler( peripheral::ISerialPort& serial_port) { chip_select.off(); data_ready = ( sizeof( rx_buff ) >= num_words ) && serial_port.receive( rx_buff ); } bool driver::SpiBus::SpiBusMaster::is_response_ready( SpiAbonent* spi_abonent ) { if( active_abonent && active_abonent == spi_abonent ) { return serial_port.isDataReady() ? spi_abonent->transmite_complete_handler( serial_port ), true : false; } return false; }