207 lines
5.5 KiB
C++
207 lines
5.5 KiB
C++
#include "../memories/memories.hh"
|
|
|
|
/*
|
|
* SpiBus.cpp
|
|
*
|
|
* Created on: 6 èþí. 2019 ã.
|
|
* Author: krugliy
|
|
*/
|
|
|
|
#include "SpiBus.hh"
|
|
|
|
#include <climits>
|
|
|
|
#include <stdint.h>
|
|
|
|
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<SpiAbonent *>( 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<SpiAbonent *>( prev_abonenet ) != spi_abonent && prev_abonenet ) {
|
|
|
|
collision.putLink( static_cast<SpiAbonent *>( 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<SpiAbonent>( *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<const char*>(bit_stream);
|
|
bit_stream = static_cast<const char*>(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<unsigned char *>(data)[i] = ~rx_buff[i];
|
|
} else {
|
|
for( int i = 0; i < num_words; i++ )
|
|
static_cast<unsigned char *>(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;
|
|
|
|
}
|