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;
|
||
|
||
}
|