mirror of https://github.com/ArduPilot/ardupilot
1165 lines
38 KiB
C++
1165 lines
38 KiB
C++
/*
|
||
wrapper for OSD code (https://github.com/night-ghost/minimosd-extra) to run in the HAL as independent process
|
||
|
||
(c) night_ghost@ykoctpa.ru 2017
|
||
|
||
*/
|
||
|
||
#include <AP_HAL/AP_HAL.h>
|
||
|
||
#ifdef BOARD_OSD_CS_PIN
|
||
|
||
#include <utility>
|
||
|
||
#include "osd_core/compat.h"
|
||
|
||
|
||
|
||
using namespace F4Light;
|
||
|
||
#include <AP_Common/AP_Common.h>
|
||
#include <stdio.h>
|
||
|
||
#include <hal.h>
|
||
#include "ring_buffer.h"
|
||
|
||
#include <inttypes.h>
|
||
#include <AP_HAL/AP_HAL.h>
|
||
#include <AP_HAL/utility/print_vprintf.h>
|
||
#include <AP_HAL_F4Light/AP_HAL_F4Light.h>
|
||
#include <AP_HAL_F4Light/SPIDevice.h>
|
||
|
||
#define SLAVE_BUILD
|
||
|
||
#include "osd_core/Defs.h"
|
||
|
||
#include "osd.h"
|
||
|
||
|
||
#include "osd_eeprom.h"
|
||
#include "osd_core/eeprom.h"
|
||
#include "osd_core/version.h"
|
||
#include <sd/SD.h>
|
||
|
||
|
||
namespace OSDns {
|
||
|
||
#include "osd_core/GCS_MAVLink.h"
|
||
|
||
#include "osd_core/OSD_Max7456.h"
|
||
OSD osd; //OSD object
|
||
|
||
#include "osd_core/prototypes.h"
|
||
#include "osd_core/Vars.h"
|
||
|
||
|
||
|
||
#include "osd_core/Config_Func.h"
|
||
#include "osd_core/Config.h"
|
||
#include "osd_core/Func.h"
|
||
#include "osd_core/protocols.h"
|
||
#include "osd_core/misc.h"
|
||
|
||
#include "osd_core/Params.h"
|
||
#include "osd_core/Panels.h"
|
||
|
||
|
||
// TODO: чтение конфига и еепром с карты памяти, чтобы закинуть .mcm и .osd и все
|
||
static const char * const words[] = {
|
||
"Air Speed", // 1
|
||
"Altitude", // 2
|
||
"Auto Mode", // 3
|
||
"Auto Screen Switch", // 4
|
||
"batt_a_k", // 5
|
||
"BattB", // 6
|
||
"batt_b_k", // 7
|
||
"Battery", // 8
|
||
"Battery A", // 9
|
||
"Battery B", // 10
|
||
"Battery Percent", // 11
|
||
"Battery Warning Level", // 12
|
||
"Call Sign", // 13
|
||
"Chanel Rotation Switching", // 14
|
||
"Channel Raw", // 15
|
||
"Channel Scale", // 16
|
||
"Channel state", // 17
|
||
"Channel Value", // 18
|
||
|
||
"Configuration", // 19
|
||
"Current", // 20
|
||
"curr_k", // 21
|
||
"Efficiency", // 22
|
||
"fBattA", // 23
|
||
"fBattB", // 24
|
||
"fCurr", // 25
|
||
"fILS", // 26
|
||
"flgHUD", // 27
|
||
"flgOnce", // 28
|
||
"flgTrack", // 29
|
||
"Flight Data", // 30
|
||
"Flight Mode", // 31
|
||
"fRussianHUD", // 32
|
||
"GPS Coord", // 33
|
||
"GPS HDOP", // 34
|
||
"Heading", // 35
|
||
"Heading Rose", // 36
|
||
"Home Altitude", // 37
|
||
"Home Direction", // 38
|
||
"Home Distance", // 39
|
||
"Horizon", // 40
|
||
"HOS", // 41
|
||
"Message", // 42
|
||
"Model Type", // 43
|
||
"NSCREENS", // 44
|
||
"OSD Brightness", // 45
|
||
"Overspeed", // 46
|
||
|
||
"Panel", // 47
|
||
"Pitch", // 48
|
||
"pitch_k", // 49
|
||
"pitch_kn", // 50
|
||
"PWMDST", // 51
|
||
"PWMSRC", // 52
|
||
"Radar Scale", // 53
|
||
"Real heading", // 54
|
||
"Roll", // 55
|
||
"roll_k", // 56
|
||
"roll_kn", // 57
|
||
"RSSI", // 58
|
||
"RSSI Enable Raw", // 59
|
||
"RSSI High", // 60
|
||
"rssi_k", // 61
|
||
"RSSI Low", // 62
|
||
"RSSI Warning Level", // 63
|
||
"SAdd1", // 64
|
||
"SAdd2", // 65
|
||
"SAdd3", // 66
|
||
"SAdd4", // 67
|
||
"Sensor 1", // 68
|
||
"Sensor 2", // 69
|
||
"Sensor 3", // 70
|
||
"Sensor 4", // 71
|
||
"SFactor1", // 72
|
||
"SFactor2", // 73
|
||
"SFactor3", // 74
|
||
"SFactor4", // 75
|
||
"SFormat1", // 76
|
||
"SFormat2", // 77
|
||
"SFormat3", // 78
|
||
"SFormat4", // 79
|
||
"Stall", // 80
|
||
"Temperature", // 81
|
||
"Throttle", // 82
|
||
"Time", // 83
|
||
"Toggle Channel", // 84
|
||
"Trip Distance", // 85
|
||
"Tune", // 86
|
||
"txtTime0", // 87
|
||
"txtTime1", // 88
|
||
"txtTime2", // 89
|
||
"txtTime3", // 90
|
||
"Units", // 91
|
||
"Velocity", // 92
|
||
"Vertical Speed", // 93
|
||
"Video Mode", // 94
|
||
"Visible Sats", // 95
|
||
"VOS", // 96
|
||
"Warnings", // 97
|
||
"Wind Speed", // 98
|
||
"WP Direction", // 99
|
||
"WP Distance", // 100
|
||
"#", // 101
|
||
"Power", // 102
|
||
"Date", // 103
|
||
"Time of day", // 104
|
||
"Motors", // 105
|
||
"Vibrations", // 106
|
||
"Variometer", // 107
|
||
"GPS Coord Lat", // 108
|
||
"GPS Coord Lon", // 109
|
||
};
|
||
|
||
typedef struct OSD_PAN {
|
||
uint8_t dst;
|
||
uint8_t size;
|
||
char fmt;
|
||
uint8_t pan_n;
|
||
} OSD_pan;
|
||
|
||
#define m1 ((uint8_t)-1)
|
||
|
||
static const OSD_pan pan_tbl[]={
|
||
{ 0, 0, 0, 0, },
|
||
{ 0, 0, 0, ID_of(airSpeed), }, // "Air Speed", // 1
|
||
{ 0, 0, 0, ID_of(alt), }, // "Altitude", // 2
|
||
{ 9, m1, 0, 0, }, // "Auto Mode", // 3 - bit in flags
|
||
{ 7, m1, 0, 0, }, // "Auto Screen Switch", // 4
|
||
{ offsetof(Settings,evBattA_koef), sizeof(sets.evBattA_koef), 'f', 0, }, // "batt_a_k",
|
||
{ offsetof(Settings,battBv), sizeof(sets.battBv), 'b', 0, }, // "BattB",
|
||
{ offsetof(Settings,evBattB_koef), sizeof(sets.evBattB_koef), 'f', 0, }, // "batt_b_k",
|
||
{ offsetof(Settings,battv), sizeof(sets.battv), 'b', 0, }, // "Battery",
|
||
{ 0, 0, 0, ID_of(batt_A), }, // "Battery A", // 9
|
||
{ 0, 0, 0, ID_of(batt_B), }, // "Battery B", // 10
|
||
{ 0, 0, 0, ID_of(batteryPercent), },// "Battery Percent", // 11
|
||
{ offsetof(Settings,batt_warn_level), sizeof(sets.batt_warn_level),'b', 0, }, // "Battery Warning Level", // 12
|
||
{ offsetof(Settings,OSD_CALL_SIGN), sizeof(sets.OSD_CALL_SIGN), 'c', 0, }, // "Call Sign", // 13
|
||
{ offsetof(Settings,switch_mode), sizeof(sets.switch_mode), 'b', 0, }, // "Chanel Rotation Switching", // 14
|
||
{ 0, 0, 0, ID_of(ch), }, // "Channel Raw", // 15
|
||
{ 0, 0, 0, ID_of(Scale), }, // "Channel Scale", // 16
|
||
{ 0, 0, 0, ID_of(State), }, // "Channel state", // 17
|
||
{ 0, 0, 0, ID_of(CValue), }, // "Channel Value", // 18
|
||
|
||
{ 0, 0, 0, 0, }, // "Configuration", // 19
|
||
{ 0, 0, 0, ID_of(curr_A), }, // "Current", // 20
|
||
{ offsetof(Settings,eCurrent_koef), sizeof(sets.eCurrent_koef), 'f', 0, }, // "curr_k", // 21
|
||
{ 0, 0, 0, ID_of(eff), }, // "Efficiency", // 22
|
||
{ 4, m1, 0, 0, }, // "fBattA", // 23
|
||
{ 5, m1, 0, 0, }, // "fBattB", // 24
|
||
{ 6, m1, 0, 0, }, // "fCurr", // 25
|
||
{ 0, 0, 0, 0, }, // 26
|
||
{ 0, 0, 0, 0, }, // 27
|
||
{ 0, m1, 0, 0, }, // "flgOnce", // 28
|
||
{ 0, 0, 0, 0, }, //29
|
||
{ 0, 0, 0, ID_of(Fdata), }, // "Flight Data", // 30
|
||
{ 0, 0, 0, ID_of(FMod), }, // "Flight Mode", // 31
|
||
{ 0, 0, 0, 0, }, // "fRussianHUD", // 32
|
||
{ 0, 0, 0, ID_of(GPS), }, // "GPS Coord", // 33
|
||
{ 0, 0, 0, ID_of(Hdop), }, // "GPS HDOP", // 34
|
||
{ 0, 0, 0, ID_of(heading), }, // "Heading", // 35
|
||
{ 0, 0, 0, ID_of(rose), }, // "Heading Rose", // 36
|
||
{ 0, 0, 0, ID_of(homeAlt), }, // "Home Altitude", // 37
|
||
{ 0, 0, 0, ID_of(homeDir), }, // "Home Direction", // 38
|
||
{ 0, 0, 0, ID_of(homeDist), }, // "Home Distance", // 39
|
||
{ 0, 0, 0, ID_of(horizon), }, // "Horizon", // 40
|
||
{ offsetof(Settings,horiz_offs), sizeof(sets.horiz_offs), 'b', 0, }, // "HOS", // 41
|
||
{ 0, 0, 0, ID_of(message), }, // "Message", // 42
|
||
{ offsetof(Settings,model_type), sizeof(sets.model_type), 'b', 0, }, // "Model Type", // 43
|
||
{ offsetof(Settings,n_screens), sizeof(sets.n_screens), 'b', 0, }, // "NSCREENS", // 44
|
||
{ offsetof(Settings,OSD_BRIGHTNESS), sizeof(sets.OSD_BRIGHTNESS),'b', 0, }, // "OSD Brightness", // 45
|
||
{ offsetof(Settings,overspeed), sizeof(sets.overspeed), 'b', 0, }, // "Overspeed", // 46
|
||
|
||
{ 0, 0, 0, 0, }, // "Panel", // 47
|
||
{ 0, 0, 0, ID_of(pitch), }, // "Pitch", // 48
|
||
{ offsetof(Settings,horiz_kPitch), sizeof(sets.horiz_kPitch), 'f', 0, }, // "pitch_k", // 49
|
||
{ offsetof(Settings,horiz_kPitch_a), sizeof(sets.horiz_kPitch_a),'f', 0, }, // "pitch_kn", // 50
|
||
{ offsetof(Settings,pwm_dst), sizeof(sets.pwm_dst), 'b', 0, }, // "PWMDST", // 51
|
||
{ offsetof(Settings,pwm_src), sizeof(sets.pwm_src), 'b', 0, }, // "PWMSRC", // 52
|
||
{ 0, 0, 0, ID_of(RadarScale), }, // "Radar Scale", // 53
|
||
{ 0, 0, 0, ID_of(COG), }, // "Real heading", // 54
|
||
{ 0, 0, 0, ID_of(roll), }, // "Roll", // 55
|
||
{ offsetof(Settings,horiz_kRoll), sizeof(sets.horiz_kRoll), 'f', 0, }, // "roll_k", // 56
|
||
{ offsetof(Settings,horiz_kRoll_a), sizeof(sets.horiz_kRoll_a), 'f', 0, }, // "roll_kn", // 57
|
||
{ 0, 0, 0, ID_of(RSSI), }, // "RSSI", // 58
|
||
{ offsetof(Settings,RSSI_raw), sizeof(sets.RSSI_raw), 'b', 0, }, // "RSSI Enable Raw", // 59
|
||
{ offsetof(Settings,RSSI_16_high), sizeof(sets.RSSI_16_high), 'w', 0, }, // "RSSI High", // 60
|
||
{ offsetof(Settings,eRSSI_koef), sizeof(sets.eRSSI_koef), 'f', 0, }, // "rssi_k", // 61
|
||
{ offsetof(Settings,RSSI_16_low), sizeof(sets.RSSI_16_low), 'w', 0, }, // "RSSI Low", // 62
|
||
{ offsetof(Settings,rssi_warn_level),sizeof(sets.rssi_warn_level),'b', 0, }, // "RSSI Warning Level", // 63
|
||
{ 0, 0, 0, 0, }, // "SAdd1", // 64 // sensors not supported
|
||
{ 0, 0, 0, 0, }, // "SAdd2", // 65
|
||
{ 0, 0, 0, 0, }, // "SAdd3", // 66
|
||
{ 0, 0, 0, 0, }, // "SAdd4", // 67
|
||
{ 0, 0, 0, ID_of(sensor1), }, // "Sensor 1", // 68
|
||
{ 0, 0, 0, ID_of(sensor2), }, // "Sensor 2", // 69
|
||
{ 0, 0, 0, ID_of(sensor3), }, // "Sensor 3", // 70
|
||
{ 0, 0, 0, ID_of(sensor4), }, // "Sensor 4", // 71
|
||
{ 0, 0, 0, 0, }, // "SFactor1", // 72
|
||
{ 0, 0, 0, 0, }, // "SFactor2", // 73
|
||
{ 0, 0, 0, 0, }, // "SFactor3", // 74
|
||
{ 0, 0, 0, 0, }, // "SFactor4", // 75
|
||
{ 0, 0, 0, 0, }, // "SFormat1", // 76
|
||
{ 0, 0, 0, 0, }, // "SFormat2", // 77
|
||
{ 0, 0, 0, 0, }, // "SFormat3", // 78
|
||
{ 0, 0, 0, 0, }, // "SFormat4", // 79
|
||
{ offsetof(Settings,stall), sizeof(sets.stall), 'b', 0, }, // "Stall", // 80
|
||
{ 0, 0, 0, ID_of(temp), }, // "Temperature", // 81
|
||
{ 0, 0, 0, ID_of(throttle), }, // "Throttle", // 82
|
||
{ 0, 0, 0, ID_of(time), }, // "Time", // 83
|
||
{ offsetof(Settings,ch_toggle), sizeof(sets.ch_toggle), 'b', 0, }, // "Toggle Channel", // 84
|
||
{ 0, 0, 0, ID_of(distance), }, // "Trip Distance", // 85
|
||
{ 0, 0, 0, ID_of(tune), }, // "Tune", // 86
|
||
{ 0, 0, 0, 0, }, // "txtTime0", // 87
|
||
{ 0, 0, 0, 0, }, // "txtTime1", // 88
|
||
{ 0, 0, 0, 0, }, // "txtTime2", // 89
|
||
{ 0, 0, 0, 0, }, // "txtTime3", // 90
|
||
{ 1, m1, 0, 0, }, // "Units", // 91
|
||
{ 0, 0, 0, ID_of(vel), }, // "Velocity", // 92
|
||
{ 0, 0, 0, ID_of(climb), }, // "Vertical Speed", // 93
|
||
{ 3, m1, 0, 0, }, // "Video Mode", // 94
|
||
{ 0, 0, 0, ID_of(GPS_sats), }, // "Visible Sats", // 95
|
||
{ offsetof(Settings,vert_offs), sizeof(sets.vert_offs), 'b', 0, }, // "VOS", // 96
|
||
{ 0, 0, 0, ID_of(warn), }, // "Warnings", // 97
|
||
{ 0, 0, 0, ID_of(windSpeed), }, // "Wind Speed", // 98
|
||
{ 0, 0, 0, ID_of(WP_dir), }, // "WP Direction", // 99
|
||
{ 0, 0, 0, ID_of(WP_dist), }, // "WP Distance", // 100
|
||
{ 0, 0, 0, 0, }, // #
|
||
{ 0, 0, 0, ID_of(Power), }, // "Power", // 102
|
||
{ 0, 0, 0, ID_of(fDate), }, // "Date", // 103
|
||
{ 0, 0, 0, ID_of(dayTime), }, // "Time of day", // 104
|
||
{ 0, 0, 0, ID_of(pMotor), }, // "Motors", // 105
|
||
{ 0, 0, 0, ID_of(fVibe), }, // "Vibrations", // 106
|
||
{ 0, 0, 0, ID_of(fVario), }, // "Variometer", // 107
|
||
{ 0, 0, 0, ID_of(coordLat), }, // "GPS Coord Lat", // 108
|
||
{ 0, 0, 0, ID_of(coordLon), }, // "GPS Coord Lon", // 109
|
||
|
||
};
|
||
|
||
static ring_buffer osd_rxrb IN_CCM;
|
||
static uint8_t osd_rx_buf[OSD_RX_BUF_SIZE] IN_CCM;
|
||
|
||
static ring_buffer osd_txrb IN_CCM;
|
||
static uint8_t osd_tx_buf[OSD_TX_BUF_SIZE] IN_CCM;
|
||
|
||
AP_HAL::OwnPtr<F4Light::SPIDevice> osd_spi;
|
||
AP_HAL::Semaphore *osd_spi_sem;
|
||
|
||
//static volatile byte vas_vsync=false;
|
||
|
||
mavlink_system_t mavlink_system = {12,1}; // sysid, compid
|
||
|
||
|
||
#ifdef OSD_DMA_TRANSFER
|
||
#define DMA_BUFFER_SIZE 510
|
||
static uint8_t dma_buffer[DMA_BUFFER_SIZE+1]; // in RAM for DMA
|
||
static uint16_t dma_transfer_length IN_CCM;
|
||
|
||
static uint8_t shadowbuf[sizeof(OSD::osdbuf)] IN_CCM;
|
||
|
||
#endif
|
||
|
||
static bool diff_done;
|
||
|
||
|
||
extern void heartBeat();
|
||
extern void writePanels();
|
||
|
||
void On100ms() {}
|
||
void On20ms() {}
|
||
void osd_loop();
|
||
void vsync_ISR();
|
||
void max_do_transfer(const char *buffer, uint16_t len);
|
||
|
||
static void max7456_cs_off(){
|
||
osd_spi->wait_busy(); // wait for transfer complete
|
||
|
||
const stm32_pin_info &pp = PIN_MAP[BOARD_OSD_CS_PIN];
|
||
gpio_write_bit(pp.gpio_device, pp.gpio_bit, HIGH);
|
||
|
||
delay_ns100(3);
|
||
}
|
||
|
||
static void max7456_cs_on(){
|
||
const stm32_pin_info &pp = PIN_MAP[BOARD_OSD_CS_PIN];
|
||
gpio_write_bit(pp.gpio_device, pp.gpio_bit, LOW);
|
||
delay_ns100(1);
|
||
}
|
||
|
||
|
||
static uint32_t sem_count=0;
|
||
|
||
void max7456_on(){
|
||
|
||
max7456_cs_on();
|
||
|
||
osd_spi->set_speed(AP_HAL::Device::SPEED_HIGH);
|
||
}
|
||
|
||
static void max7456_sem_on(){
|
||
|
||
if(osd_spi_sem->take(HAL_SEMAPHORE_BLOCK_FOREVER)) {
|
||
max7456_on();
|
||
}
|
||
}
|
||
|
||
void max7456_off(){
|
||
max7456_cs_off();
|
||
}
|
||
|
||
static void max7456_sem_off(){
|
||
max7456_off();
|
||
osd_spi_sem->give(); // give sem on last count
|
||
}
|
||
|
||
void MAX_write(byte addr, byte data){
|
||
max7456_cs_on();
|
||
osd_spi->transfer(addr); // this transfer don't controls CS
|
||
osd_spi->transfer(data);
|
||
max7456_cs_off();
|
||
}
|
||
|
||
byte MAX_read(byte addr){
|
||
max7456_cs_on();
|
||
osd_spi->transfer(addr); // this transfer don't controls CS
|
||
uint8_t ret = osd_spi->transfer(0xff);
|
||
max7456_cs_off();
|
||
return ret;
|
||
}
|
||
|
||
byte MAX_rw(byte b){
|
||
max7456_cs_on();
|
||
uint8_t ret=osd_spi->transfer(b);
|
||
max7456_cs_off();
|
||
return ret;
|
||
}
|
||
|
||
static uint16_t rdb_ptr IN_CCM;
|
||
|
||
|
||
#ifdef OSD_DMA_TRANSFER
|
||
static void prepare_dma_buffer(){
|
||
uint16_t rp;
|
||
uint16_t wp=0;
|
||
|
||
uint8_t last_h=0xff;
|
||
|
||
// MAX_write(MAX7456_DMM_reg, 0);
|
||
// MAX_write(MAX7456_VM1_reg, B01000111);
|
||
|
||
memset(dma_buffer,0xff,sizeof(dma_buffer));
|
||
|
||
dma_buffer[wp++] = MAX7456_DMM_reg; dma_buffer[wp++] = 0;
|
||
dma_buffer[wp++] = MAX7456_VM1_reg; dma_buffer[wp++] = B01000111;
|
||
|
||
|
||
// сначала все изменения
|
||
for(rp=0; rp<MAX7456_screen_size ; rp++){
|
||
uint8_t c = OSD::osdbuf[rp];
|
||
if(c != shadowbuf[rp] ){
|
||
if(wp>=DMA_BUFFER_SIZE-6) break;
|
||
uint8_t h = rp>>8;
|
||
if(last_h != h){
|
||
last_h = h;
|
||
dma_buffer[wp++] = MAX7456_DMAH_reg; dma_buffer[wp++] = h;
|
||
if(wp>=DMA_BUFFER_SIZE-6) break;
|
||
}
|
||
|
||
dma_buffer[wp++] = MAX7456_DMAL_reg; dma_buffer[wp++] = rp&0xFF;
|
||
dma_buffer[wp++] = MAX7456_DMDI_reg; dma_buffer[wp++] = c;
|
||
shadowbuf[rp] = c;
|
||
}
|
||
}
|
||
|
||
// а в оставшееся место все остальное по кольцу. таким образом пересылка у нас всегда 500 байт, и на частоте 4.5МГц занимает ~1ms.
|
||
// длинные пересылки имеют низкий приоритет, и никому не мешают
|
||
while(wp<DMA_BUFFER_SIZE-6){
|
||
uint8_t c = OSD::osdbuf[rdb_ptr];
|
||
uint8_t h = rdb_ptr>>8;
|
||
if(last_h != h){
|
||
last_h = h;
|
||
dma_buffer[wp++] = MAX7456_DMAH_reg; dma_buffer[wp++] = h;
|
||
if(wp>=DMA_BUFFER_SIZE-6) break;
|
||
}
|
||
|
||
dma_buffer[wp++] = MAX7456_DMAL_reg; dma_buffer[wp++] = rdb_ptr&0xFF;
|
||
dma_buffer[wp++] = MAX7456_DMDI_reg; dma_buffer[wp++] = c;
|
||
shadowbuf[rdb_ptr] = c;
|
||
rdb_ptr++;
|
||
if(rdb_ptr >= MAX7456_screen_size) rdb_ptr=0; // loop
|
||
}
|
||
|
||
// dma_buffer[wp++] = MAX7456_VM0_reg; dma_buffer[wp++] = MAX7456_ENABLE_display | MAX7456_SYNC_autosync | OSD::video_mode;
|
||
|
||
dma_transfer_length = wp;
|
||
diff_done = true;
|
||
}
|
||
#endif
|
||
|
||
|
||
uint32_t get_word(char *buf, char * &ptr){
|
||
uint32_t sel_len=0;
|
||
uint8_t sel_id=0;
|
||
|
||
for(uint32_t i=0; i<ARRAY_SIZE(words); i++){
|
||
uint32_t len=strlen(words[i]);
|
||
if(strncmp(buf, words[i],len)==0){
|
||
if(len > sel_len) { // longest match
|
||
sel_len = len;
|
||
sel_id = i+1;
|
||
}
|
||
}
|
||
}
|
||
ptr=buf+sel_len;
|
||
return sel_id;
|
||
}
|
||
|
||
char * get_lex(char * &ptro){
|
||
char *ptr;
|
||
char *buf = ptro;
|
||
while(*buf && (*buf=='\t' || *buf == ' ')) buf++;
|
||
ptr=buf;
|
||
while(*ptr && *ptr!='\t') ptr++;
|
||
if(*ptr==0) {
|
||
ptro=NULL;
|
||
} else {
|
||
*ptr=0;
|
||
ptro=ptr+1;
|
||
}
|
||
return buf;
|
||
|
||
}
|
||
|
||
static bool get_flag(char *p) {
|
||
if(!p) return false;
|
||
|
||
if(*p=='T' || *p=='t' || *p=='1') return true;
|
||
return false;
|
||
}
|
||
|
||
// x, y, vis, sign, Altf, Alt2, Alt3, Alt4, strings
|
||
// 30 15 False 0 1 1 1 1 A||||
|
||
static point create_point(char *px, char *py, char *pVis, char *pSign, char *pAlt, char *pAlt2, char *pAlt3, char *pAlt4, char *ps ){
|
||
point p;
|
||
p.x = strtoul(px, nullptr, 10);
|
||
p.y = strtoul(py, nullptr, 10);
|
||
p = do_on(p, get_flag(pVis));
|
||
p = do_sign(p, get_flag(pSign));
|
||
if(get_flag(pAlt)) p=do_alt(p);
|
||
if(get_flag(pAlt2)) p=do_alt2(p);
|
||
if(get_flag(pAlt3)) p=do_alt3(p);
|
||
if(get_flag(pAlt4)) p=do_alt4(p);
|
||
|
||
// if(ps) collect_strings(ps); not supported
|
||
return p;
|
||
}
|
||
|
||
#define write_point(n,p) eeprom_write_len((byte *)&p, OffsetBITpanel * (int)panel_num + n * sizeof(Point), sizeof(Point) );
|
||
|
||
static void load_config(){
|
||
File fd = SD.open("eeprom.osd", FILE_READ);
|
||
if (fd) {
|
||
printf("\nLoading OSD config\n");
|
||
char buf[80];
|
||
// memset(buf, 0, sizeof(buf));
|
||
uint32_t panel_num=-1;
|
||
bool is_config=false;
|
||
|
||
while(fd.gets(buf, sizeof(buf)-1) > 0) {
|
||
// we readed one line
|
||
char *ptr;
|
||
char *p[10];
|
||
|
||
uint8_t word=get_word(buf,ptr);
|
||
switch(word) {
|
||
case 0: // not found
|
||
case 101: // #
|
||
continue;
|
||
|
||
case 47: { // panel
|
||
char *p2 = get_lex(ptr);
|
||
panel_num=strtoul(p2, nullptr, 10);
|
||
uint16_t flags=strtoul(ptr, nullptr, 10);
|
||
write_point(0,flags);
|
||
}break;
|
||
|
||
case 19: // config
|
||
is_config=true;
|
||
break;
|
||
|
||
default:
|
||
char **pp = p;
|
||
memset(p,0,sizeof(p));
|
||
do {
|
||
*pp++ = get_lex(ptr);
|
||
}while(ptr);
|
||
|
||
|
||
if(is_config){
|
||
if(pan_tbl[word].size == (uint8_t)-1){ // bit flags
|
||
uint32_t flags = sets.flags.dw;
|
||
if(get_flag(p[0])) flags |= (1<<pan_tbl[word].dst);
|
||
else flags &= ~(1<<pan_tbl[word].dst);
|
||
sets.flags.dw=flags;
|
||
} else {
|
||
union {
|
||
float f;
|
||
uint8_t b;
|
||
char buf[8];
|
||
uint16_t w;
|
||
} val;
|
||
|
||
switch(pan_tbl[word].fmt){
|
||
case 'f': // float
|
||
val.f = atof(p[0]);
|
||
break;
|
||
case 'b': // byte
|
||
val.b=(uint8_t)strtoul(p[0], nullptr, 10);
|
||
break;
|
||
case 'c': // char
|
||
strncpy(val.buf, p[0], 8);
|
||
break;
|
||
case 'w':
|
||
val.w=(uint16_t)strtoul(p[0], nullptr, 10);
|
||
break;
|
||
default:
|
||
continue; // ignore this line
|
||
}
|
||
memmove( ((uint8_t*)(&sets)) + pan_tbl[word].dst, &val, pan_tbl[word].size);
|
||
eeprom_write_len(((uint8_t*)(&sets)) + pan_tbl[word].dst, EEPROM_offs(sets) + pan_tbl[word].dst, pan_tbl[word].size);
|
||
}
|
||
}else{ // panel
|
||
uint8_t id = pan_tbl[word].pan_n;
|
||
if(id) {
|
||
// 30 15 False 0 1 1 1 1 A||||
|
||
point po=create_point(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8] );
|
||
|
||
write_point(id, po); // save to eeprom
|
||
}
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
}
|
||
fd.close();
|
||
|
||
// we don't save flags when parsing so lets do it now
|
||
eeprom_write_len( &sets.flags.pad[0], EEPROM_offs(sets) + ((byte *)&sets.flags.pad - (byte *)&sets), sizeof(sets.flags.pad));
|
||
|
||
readSettings(); // re-read new values back, for settings that not in config.osd get all-1 state
|
||
|
||
sets.CHK1_VERSION = VER; // set version - EEPROM OK
|
||
sets.CHK2_VERSION = (VER ^ 0x55);
|
||
eeprom_write_len( &sets.CHK1_VERSION, EEPROM_offs(sets) + ((byte *)&sets.CHK1_VERSION - (byte *)&sets), 2 );
|
||
}
|
||
}
|
||
|
||
static void load_font(){
|
||
const char font[]="font.mcm";
|
||
File fd = SD.open(font, FILE_READ);
|
||
if (fd) {
|
||
char buf[80];
|
||
|
||
if(fd.gets(buf, sizeof(buf)-1)){
|
||
printf("\nLoading OSD font\n");
|
||
|
||
OSD::setPanel(5, 5);
|
||
osd_print_S(PSTR("font uploading "));
|
||
OSD::update();// Show it
|
||
|
||
char patt[]="MAX7456";
|
||
|
||
if(strncmp(buf,patt,strlen(patt))==0){
|
||
uint8_t character_bitmap[0x40];
|
||
|
||
uint16_t font_count = 0;
|
||
byte byte_count = 0;
|
||
byte bit_count=0;
|
||
|
||
uint8_t chk=0;
|
||
uint8_t c=0;
|
||
uint8_t last_c;
|
||
|
||
uint8_t b=0;
|
||
|
||
uint8_t cnt=0;
|
||
while(font_count < 256) {
|
||
last_c = c;
|
||
if(fd.read(&c,1)<=0) break; // get a char
|
||
|
||
switch(c){ // parse and decode mcm file
|
||
case 0x0A: // line feed - skip
|
||
if(last_c == 0x0d) continue;
|
||
// lf without cr cause line end
|
||
|
||
case 0x0d: // carridge return, end of line
|
||
if (bit_count == 8) {
|
||
chk ^= b;
|
||
character_bitmap[byte_count] = b;
|
||
b = 0;
|
||
byte_count++;
|
||
}
|
||
bit_count = 0;
|
||
break;
|
||
|
||
case 0x30: // ascii '0'
|
||
case 0x31: // ascii '1'
|
||
b <<= 1;
|
||
if(c == 0x31)
|
||
b += 1;
|
||
bit_count++;
|
||
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
// we have one completed character
|
||
// write the character to NVM
|
||
if(byte_count == 64) {
|
||
osd.write_NVM(font_count, character_bitmap);
|
||
byte_count = 0;
|
||
font_count++;
|
||
printf(".");
|
||
chk=0;
|
||
}
|
||
}
|
||
printf("done\n");
|
||
}
|
||
}
|
||
|
||
fd.close();
|
||
//* not in debug mode
|
||
SD.remove(font); // once!
|
||
//*/
|
||
}
|
||
|
||
}
|
||
|
||
static bool osd_need_redraw = false;
|
||
static void * task_handle;
|
||
|
||
// slowly write all buffer
|
||
static void write_buff_to_MAX(bool all){
|
||
|
||
max7456_on();
|
||
MAX_write(MAX7456_DMM_reg, 0);
|
||
|
||
// clear internal memory
|
||
uint8_t old_h=0xff;
|
||
for(uint16_t len = MAX7456_screen_size, cnt=0;len--; cnt++){
|
||
uint8_t c= OSD::osdbuf[cnt];
|
||
if(all || c!=0x20) {
|
||
max7456_cs_on();
|
||
uint8_t h = cnt>>8;
|
||
if(old_h!=h){
|
||
MAX_write(MAX7456_DMAH_reg, h);
|
||
old_h = h;
|
||
}
|
||
MAX_write(MAX7456_DMAL_reg, cnt&0xFF);
|
||
MAX_write(MAX7456_DMDI_reg, c);
|
||
max7456_cs_off();
|
||
}
|
||
#ifdef OSD_DMA_TRANSFER
|
||
shadowbuf[cnt] = c;
|
||
#endif
|
||
}
|
||
max7456_off();
|
||
}
|
||
|
||
|
||
|
||
void osd_begin(AP_HAL::OwnPtr<F4Light::SPIDevice> spi){
|
||
|
||
osd_spi = std::move(spi);
|
||
|
||
osd_spi_sem = osd_spi->get_semaphore(); // bus semaphore
|
||
{
|
||
const stm32_pin_info &pp = PIN_MAP[BOARD_OSD_CS_PIN];
|
||
gpio_set_mode(pp.gpio_device, pp.gpio_bit, GPIO_OUTPUT_PP);
|
||
gpio_set_speed(pp.gpio_device, pp.gpio_bit, GPIO_speed_100MHz);
|
||
gpio_write_bit(pp.gpio_device, pp.gpio_bit, HIGH);
|
||
}
|
||
|
||
#ifdef BOARD_OSD_RESET_PIN
|
||
{
|
||
const stm32_pin_info &pp = PIN_MAP[BOARD_OSD_RESET_PIN];
|
||
gpio_set_mode(pp.gpio_device, pp.gpio_bit, GPIO_OUTPUT_PP);
|
||
gpio_set_speed(pp.gpio_device, pp.gpio_bit, GPIO_speed_25MHz);
|
||
gpio_write_bit(pp.gpio_device, pp.gpio_bit, LOW);
|
||
delayMicroseconds(50);
|
||
gpio_write_bit(pp.gpio_device, pp.gpio_bit, HIGH);
|
||
delayMicroseconds(120);
|
||
}
|
||
#endif
|
||
|
||
|
||
rb_init(&osd_rxrb, OSD_RX_BUF_SIZE, osd_rx_buf);
|
||
rb_init(&osd_txrb, OSD_TX_BUF_SIZE, osd_tx_buf);
|
||
|
||
OSD_EEPROM::init();
|
||
|
||
// clear memory
|
||
memset(OSD::osdbuf,0x20, sizeof(OSD::osdbuf));
|
||
#ifdef OSD_DMA_TRANSFER
|
||
memset(shadowbuf, 0x20, sizeof(shadowbuf));
|
||
#endif
|
||
|
||
/*
|
||
lets try to load settings from SD card
|
||
*/
|
||
load_config();
|
||
|
||
readSettings();
|
||
|
||
doScreenSwitch(); // set vars for startup screen
|
||
|
||
if( sets.CHK1_VERSION != VER || sets.CHK2_VERSION != (VER ^ 0x55)) { // wrong version
|
||
lflags.bad_config=1;
|
||
|
||
// some useful defaults
|
||
sets.OSD_BRIGHTNESS = 2;
|
||
sets.horiz_offs = 0x20;
|
||
sets.vert_offs = 0x10;
|
||
|
||
}
|
||
|
||
while(millis()<1000) { // delay initialization until video stabilizes
|
||
hal_yield(1000);
|
||
}
|
||
|
||
max7456_sem_on();
|
||
|
||
write_buff_to_MAX(true);
|
||
|
||
|
||
for(uint8_t i=0; i<100; i++) {
|
||
osd.init(); // Start display
|
||
|
||
max7456_on();
|
||
uint8_t vm0 = MAX_read(MAX7456_VM0_reg | MAX7456_reg_read); // check register
|
||
max7456_off();
|
||
|
||
uint8_t patt = MAX7456_ENABLE_display | MAX7456_SYNC_autosync | OSD::video_mode;
|
||
|
||
if(vm0==patt) break;
|
||
}
|
||
|
||
max7456_off();
|
||
|
||
load_font();
|
||
|
||
max7456_sem_off();
|
||
|
||
#define REL_1 int(RELEASE_NUM/100)
|
||
#define REL_2 int((RELEASE_NUM - REL_1*100 )/10)
|
||
#define REL_3 int((RELEASE_NUM - REL_1*100 - REL_2*10 ))
|
||
|
||
if(sets.FW_VERSION[0]!=(REL_1 + '0') || sets.FW_VERSION[1]!=(REL_2 + '0') || sets.FW_VERSION[2]!=(REL_3 + '0') ){
|
||
sets.FW_VERSION[0]=REL_1 + '0';
|
||
sets.FW_VERSION[1]=REL_2 + '0';
|
||
sets.FW_VERSION[2]=REL_3 + '0';
|
||
|
||
eeprom_write_len( sets.FW_VERSION, EEPROM_offs(sets) + ((uint8_t *)sets.FW_VERSION - (uint8_t *)&sets), sizeof(sets.FW_VERSION) );
|
||
}
|
||
|
||
logo();
|
||
|
||
|
||
#ifdef BOARD_OSD_VSYNC_PIN
|
||
Revo_hal_handler h = { .vp = vsync_ISR };
|
||
|
||
GPIO::_attach_interrupt(BOARD_OSD_VSYNC_PIN, h.h, RISING, VSI_INT_PRIORITY);
|
||
#endif
|
||
|
||
task_handle = Scheduler::start_task(OSDns::osd_loop, SMALL_TASK_STACK); //
|
||
Scheduler::set_task_priority(task_handle, OSD_LOW_PRIORITY); // less than main task
|
||
Scheduler::set_task_period(task_handle, 10000); // 100Hz
|
||
}
|
||
|
||
// all task is in one thread so no sync required
|
||
|
||
void osd_loop() {
|
||
if(osd_need_redraw){ // если была отложенная передача
|
||
osd_need_redraw=false;
|
||
|
||
OSD::update();
|
||
Scheduler::set_task_priority(task_handle, OSD_LOW_PRIORITY); // restore priority to low
|
||
}
|
||
|
||
uint32_t pt=millis();
|
||
|
||
seconds = pt / 1000;
|
||
|
||
osd_dequeue(); // we MUST parse input even in case of bad config because it is the only way to communicate
|
||
|
||
if(pt < BOOTTIME || lflags.bad_config){ // startup delay for fonts or EEPROM error
|
||
logo();
|
||
return;
|
||
}
|
||
|
||
#if defined(MAV_REQUEST) && (defined(USE_MAVLINK) || defined(USE_MAVLINKPX4))
|
||
if(apm_mav_system && !lflags.mav_request_done){ // we got HEARTBEAT packet and still don't send requests
|
||
for(uint8_t n = 3; n >0; n--){
|
||
request_mavlink_rates(); //Three times to certify it will be readed
|
||
delay_150();
|
||
}
|
||
lflags.mav_request_done=1;
|
||
}
|
||
#endif
|
||
|
||
if(lflags.got_data){ // if new data comes
|
||
|
||
pan_toggle(); // check for screen toggle
|
||
|
||
if(!lflags.need_redraw) {
|
||
lflags.need_redraw=1;
|
||
vsync_wait=1; // will wait for interrupt
|
||
}
|
||
|
||
lflags.got_data=0; // data parsed
|
||
}
|
||
|
||
if( lflags.need_redraw) {
|
||
lflags.need_redraw=0; // screen drawn
|
||
|
||
setHomeVars(); // calculate and set Distance from home and Direction to home
|
||
|
||
setFdataVars(); // statistics and min/max
|
||
|
||
writePanels(); // writing enabled panels (check OSD_Panels Tab)
|
||
|
||
#ifdef OSD_DMA_TRANSFER
|
||
prepare_dma_buffer(); // prepare diff with addresses
|
||
#endif
|
||
|
||
update_screen = 1; // data comes, wee need to redraw screen
|
||
}
|
||
|
||
if(pt > timer_20ms){
|
||
timer_20ms+=20;
|
||
On20ms();
|
||
|
||
if(update_screen && vsync_wait && (millis() - vsync_time)>50){ // interrupts stopped - more than 50 ms passed from the last one
|
||
vsync_wait=0; // хватит ждать
|
||
Scheduler::set_task_priority(task_handle, OSD_HIGH_PRIORITY); // equal to main
|
||
OSD::update(); // update compulsorily (and then update every 20ms)
|
||
update_screen = 0;
|
||
Scheduler::set_task_priority(task_handle, OSD_LOW_PRIORITY);
|
||
}
|
||
}
|
||
|
||
if(pt > timer_100ms){
|
||
timer_100ms+= 100;
|
||
On100ms();
|
||
|
||
lflags.flag_01s = !lflags.flag_01s;
|
||
|
||
if(lflags.flag_01s) {
|
||
|
||
if(skip_inc) {
|
||
skip_inc++;
|
||
|
||
if(skip_inc >=3){
|
||
count02s++;
|
||
skip_inc=0; // we go again
|
||
}
|
||
|
||
} else
|
||
count02s++;
|
||
}
|
||
// count01s++;
|
||
}
|
||
|
||
if(pt > timer_500ms){
|
||
timer_500ms+= 500;
|
||
lflags.got_data=1; // every half second forcibly
|
||
update_screen = 1;
|
||
|
||
lflags.flag_05s = 1;
|
||
|
||
count05s++;
|
||
|
||
lflags.blinker = !lflags.blinker;
|
||
if(lflags.blinker) {
|
||
// seconds++;
|
||
lflags.one_sec_timer_switch = 1; // for warnings
|
||
|
||
if(lflags.got_date) day_seconds++; // if we has GPS time - let it ticks
|
||
|
||
if( vas_vsync && vsync_count < 5) { // at a frame rate they should be 25 or 50
|
||
// but there are boards where this pin is not connected. China...
|
||
max7456_err_count++;
|
||
if(max7456_err_count>3) { // 3 seconds bad sync
|
||
#ifdef DEBUG
|
||
printf(PSTR("restart MAX! vsync_count=%d\n"),vsync_count);
|
||
#endif
|
||
osd.reset(); // restart MAX7456
|
||
}
|
||
} else max7456_err_count=0;
|
||
|
||
vsync_count=0;
|
||
|
||
heartBeat();
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
void vsync_ISR(){
|
||
vas_vsync=true;
|
||
vsync_wait=0; // note its presence
|
||
|
||
vsync_count++; // count VSYNC interrupts
|
||
vsync_time=millis(); // and note a time
|
||
|
||
if(update_screen) { // there is data for screen
|
||
osd_need_redraw=true;
|
||
Scheduler::set_task_priority(task_handle, OSD_HIGH_PRIORITY); // higher than all drivers so it will be scheduled just after semaphore release
|
||
Scheduler::task_resume(task_handle); // task should be finished at this time so resume it
|
||
update_screen = 0;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
// is there any chars in ring buffer?
|
||
int16_t osd_available(){
|
||
return rb_full_count(&osd_rxrb);
|
||
}
|
||
|
||
void osd_queue(uint8_t c) { // push bytes from OSD to FC around in the ring buffer
|
||
uint8_t cnt=10;
|
||
while(rb_is_full(&osd_rxrb)) {
|
||
hal_yield(0);
|
||
if(--cnt == 0) return; // destination don't listen
|
||
}
|
||
rb_push_insert(&osd_rxrb, c);
|
||
}
|
||
|
||
|
||
int16_t osd_getc(){ // get char from ring buffer
|
||
return rb_remove(&osd_rxrb);
|
||
}
|
||
|
||
uint32_t osd_txspace() {
|
||
return osd_txrb.size - rb_full_count(&osd_txrb);
|
||
}
|
||
|
||
|
||
void osd_putc(uint8_t c){
|
||
uint8_t cnt=10;
|
||
while(rb_is_full(&osd_txrb)) {
|
||
Scheduler::set_task_priority(task_handle, OSD_HIGH_PRIORITY); // to run in time of yield()
|
||
hal_yield(0);
|
||
if(--cnt == 0) break; // destination don't listen
|
||
}
|
||
rb_push_insert(&osd_txrb, c);
|
||
Scheduler::set_task_priority(task_handle, OSD_LOW_PRIORITY); // restore priority to low
|
||
}
|
||
|
||
void osd_dequeue() {
|
||
Scheduler::set_task_priority(task_handle, 100); // equal to main to not overflow buffers on packet decode
|
||
|
||
while(!rb_is_empty(&osd_txrb)) {
|
||
extern bool mavlink_one_byte(char c);
|
||
char c = rb_remove(&osd_txrb);
|
||
|
||
if(mavlink_one_byte(c)) lflags.got_data=true;
|
||
}
|
||
Scheduler::set_task_priority(task_handle, OSD_LOW_PRIORITY); // restore priority to low
|
||
|
||
}
|
||
|
||
|
||
static uint8_t max_err_cnt=0;
|
||
|
||
void update_max_buffer(const uint8_t *buffer, uint16_t len){
|
||
max7456_sem_on();
|
||
|
||
uint16_t cnt=0;
|
||
|
||
|
||
#if 1
|
||
uint8_t patt = MAX7456_ENABLE_display | MAX7456_SYNC_autosync | OSD::video_mode;
|
||
max7456_cs_on();
|
||
uint8_t vm0 = MAX_read(MAX7456_VM0_reg | MAX7456_reg_read);
|
||
|
||
if(vm0 != patt) {
|
||
max_err_cnt++;
|
||
if(max_err_cnt<3) {
|
||
OSD::hw_init(); // first try without reset
|
||
} else {
|
||
// 3 errors together - nothing helps :(
|
||
#ifdef BOARD_OSD_RESET_PIN
|
||
{
|
||
const stm32_pin_info &pp = PIN_MAP[BOARD_OSD_RESET_PIN];
|
||
gpio_write_bit(pp.gpio_device, pp.gpio_bit, LOW);
|
||
delayMicroseconds(50);
|
||
gpio_write_bit(pp.gpio_device, pp.gpio_bit, HIGH);
|
||
delayMicroseconds(120);
|
||
}
|
||
#endif
|
||
OSD::init();
|
||
max_err_cnt=0;
|
||
}
|
||
} else {
|
||
max_err_cnt=0;
|
||
}
|
||
|
||
MAX_write(MAX7456_DMAH_reg, 0);
|
||
MAX_write(MAX7456_DMAL_reg, 0);
|
||
MAX_write(MAX7456_DMM_reg, 1); // set address auto-increment
|
||
|
||
max7456_cs_off();
|
||
|
||
if(osd_spi->send_strobe(buffer, len)!=len) {
|
||
/*/// for debug - mark last written char
|
||
MAX_rw(0x86); // finish transfer
|
||
//*///
|
||
MAX_rw(0xff); // finish transfer
|
||
}
|
||
|
||
#elif 0
|
||
MAX_write(MAX7456_DMAH_reg, 0);
|
||
MAX_write(MAX7456_DMAL_reg, 0);
|
||
MAX_write(MAX7456_DMM_reg, 0);
|
||
|
||
// try to send just diffenence - don't clears last chars
|
||
uint8_t last_h=0;
|
||
while(len--){
|
||
if(*buffer != shadowbuf[cnt]){
|
||
uint8_t h = cnt>>8 ;
|
||
if(last_h != h){
|
||
MAX_write(MAX7456_DMAH_reg, h);
|
||
last_h = h;
|
||
}
|
||
MAX_write(MAX7456_DMAL_reg, cnt&0xFF);
|
||
MAX_write(MAX7456_DMDI_reg, *buffer);
|
||
shadowbuf[cnt] = *buffer;
|
||
}
|
||
buffer++;
|
||
cnt++;
|
||
}
|
||
|
||
#elif 0
|
||
|
||
// a try to do writes in software strobe mode
|
||
MAX_write(MAX7456_DMAH_reg, 0);
|
||
MAX_write(MAX7456_DMAL_reg, 0);
|
||
MAX_write(MAX7456_DMM_reg, 1); // set address auto-increment
|
||
max7456_cs_off();
|
||
while(len--){
|
||
max7456_cs_on();
|
||
// osd_spi->transfer(*buffer++); MAX7456
|
||
MAX_rw(*buffer++);
|
||
buffer++;
|
||
cnt++;
|
||
osd_spi->wait_busy();
|
||
max7456_cs_off();
|
||
}
|
||
max7456_cs_on();
|
||
MAX_write(MAX7456_DMM_reg, 0); // clear address auto-increment
|
||
#else
|
||
// just write all to MAX
|
||
MAX_write(MAX7456_DMAH_reg, 0);
|
||
MAX_write(MAX7456_DMAL_reg, 0);
|
||
MAX_write(MAX7456_DMM_reg, 0);
|
||
|
||
uint8_t last_h=0;
|
||
while(len--){
|
||
uint8_t h = cnt>>8 ;
|
||
if(last_h != h){
|
||
MAX_write(MAX7456_DMAH_reg, h);
|
||
last_h = h;
|
||
}
|
||
|
||
MAX_write(MAX7456_DMAL_reg, cnt&0xFF);
|
||
MAX_write(MAX7456_DMDI_reg, *buffer);
|
||
buffer++;
|
||
cnt++;
|
||
}
|
||
|
||
#endif
|
||
|
||
max7456_sem_off();
|
||
}
|
||
|
||
|
||
|
||
} // namespace
|
||
|
||
|
||
#endif |