/// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- /* (c) 2017 night_ghost@ykoctpa.ru based on: * Copyright (C) 2015 Intel Corporation. All rights reserved. * * This file is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #pragma once #include #include "Semaphores.h" #include "Scheduler.h" #include #include namespace F4Light { #define MAX_BUS_NUM 3 typedef enum SPIFrequency { SPI_18MHZ = 0, /**< 18 MHz */ SPI_9MHZ = 1, /**< 9 MHz */ SPI_4_5MHZ = 2, /**< 4.5 MHz */ SPI_2_25MHZ = 3, /**< 2.25 MHz */ SPI_1_125MHZ = 4, /**< 1.125 MHz */ SPI_562_500KHZ = 5, /**< 562.500 KHz */ SPI_281_250KHZ = 6, /**< 281.250 KHz */ SPI_140_625KHZ = 7, /**< 140.625 KHz */ SPI_36MHZ = 8, /**< 36 MHz */ } SPIFrequency; typedef uint8_t (*spi_WaitFunc)(uint8_t b); struct spi_pins { uint8_t sck; uint8_t miso; uint8_t mosi; }; typedef enum SPI_TRANSFER_MODE { SPI_TRANSFER_POLL=0, SPI_TRANSFER_DMA, SPI_TRANSFER_INTR, SPI_TRANSFER_SOFT, } SPI_transferMode; typedef struct SPIDESC { const char * const name; const spi_dev * const dev; uint8_t bus; spi_mode sm; uint16_t cs_pin; SPIFrequency lowspeed; SPIFrequency highspeed; SPI_transferMode mode; // mode of operations: 0 - polling, 1&2 DMA, 3 interrupts, 4 software uint32_t prio; // DMA priority uint8_t assert_dly; // delay after CS goes low uint8_t release_dly; // delay before CS goes high } SPIDesc; enum SPI_ISR_MODE { SPI_ISR_NONE, SPI_ISR_SEND, SPI_ISR_SEND_DMA, // want DMA but can't SPI_ISR_SKIP_RX, // wait for 1 dummy byte SPI_ISR_SKIP_RX_DMA, SPI_ISR_WAIT_RX, // wait for receiving of last fake byte from TX SPI_ISR_WAIT_RX_DMA, // wait for receiving of last fake byte from TX SPI_ISR_RECEIVE, SPI_ISR_RXTX, // full duplex SPI_ISR_STROBE, SPI_ISR_COMPARE, SPI_ISR_FINISH, }; //#define DEBUG_SPI #ifdef DEBUG_SPI // for debug typedef struct SPI_TRANS { const spi_dev * dev; uint32_t time; enum SPI_ISR_MODE mode; uint8_t act; uint8_t data; uint32_t cr2; uint32_t sr1; uint16_t send_len; uint16_t recv_len; uint16_t dummy_len; spi_WaitFunc cb; } spi_trans; #define SPI_LOG_SIZE 200 #endif class SPIDevice : public AP_HAL::SPIDevice { public: SPIDevice(const SPIDesc &device_desc); ~SPIDevice() {} /* AP_HAL::SPIDevice implementation */ bool set_speed(AP_HAL::Device::Speed speed) override; bool transfer(const uint8_t *send, uint32_t send_len, uint8_t *recv, uint32_t recv_len) override; // one byte uint8_t transfer(uint8_t out); void send(uint8_t out); // without wait for answer /* See AP_HAL::SPIDevice::transfer_fullduplex() */ bool transfer_fullduplex(const uint8_t *send, uint8_t *recv, uint32_t len) override; /* single-byte transfers don't do it */ inline void apply_speed() { spi_set_speed(_desc.dev, determine_baud_rate(_speed)); } uint16_t send_strobe(const uint8_t *buffer, uint16_t len); // send in ISR and strobe each byte by CS void wait_busy() { spi_wait_busy(_desc.dev); } uint8_t wait_for(uint8_t out, spi_WaitFunc cb, uint32_t dly); // wait for needed byte in ISR /* See AP_HAL::Device::get_semaphore() */ inline F4Light::Semaphore *get_semaphore() { uint8_t n = _desc.bus - 1; if(nwrite(0); delay_ns100(_desc.assert_dly); } } // Select device and wait a little inline void _cs_release(){ spi_wait_busy(_desc.dev); if(_cs){ delay_ns100(_desc.release_dly); _cs->write(1); } } // Deselect device after some delay const spi_pins* dev_to_spi_pins(const spi_dev *dev); spi_baud_rate determine_baud_rate(SPIFrequency freq); uint8_t _transfer(uint8_t data); void get_dma_ready(); void setup_dma_transfer(const uint8_t *send, const uint8_t *recv, uint32_t btr ); void setup_isr_transfer(); void start_dma_transfer(); uint8_t do_transfer(bool is_DMA, uint32_t nbytes); Handler _completion_cb; void *_task; // vars for send_strobe() and wait_for() const uint8_t *_send_address; uint16_t _send_len; uint16_t _dummy_len; uint8_t *_recv_address; uint16_t _recv_len; SPI_ISR_MODE _isr_mode; spi_WaitFunc _compare_cb; uint8_t _recv_data; void isr_transfer_finish(); void disable_dma(); #ifdef BOARD_SOFTWARE_SPI volatile GPIO_TypeDef *sck_port; uint16_t sck_pin; volatile GPIO_TypeDef *mosi_port; uint16_t mosi_pin; volatile GPIO_TypeDef *miso_port; uint16_t miso_pin; uint16_t dly_time; void spi_soft_set_speed(); uint8_t _transfer_s(uint8_t bt); #endif #ifdef DEBUG_SPI static spi_trans spi_trans_array[SPI_LOG_SIZE]; static uint8_t spi_trans_ptr; #endif }; class SPIDeviceManager : public AP_HAL::SPIDeviceManager { public: friend class SPIDevice; SPIDeviceManager(){ } AP_HAL::OwnPtr get_device(const char *name) { return _get_device(name); } static AP_HAL::OwnPtr _get_device(const char *name); protected: }; } // namespace