2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
31 #include "common/maths.h"
33 #include "build/debug.h"
35 #include "drivers/adc_impl.h"
36 #include "drivers/io.h"
37 #include "drivers/dma.h"
38 #include "drivers/motor_impl.h"
39 #include "drivers/serial.h"
40 #include "drivers/serial_tcp.h"
41 #include "drivers/system.h"
42 #include "drivers/time.h"
43 #include "drivers/pwm_output.h"
44 #include "drivers/light_led.h"
46 #include "drivers/timer.h"
47 #include "timer_def.h"
49 #include "drivers/accgyro/accgyro_virtual.h"
50 #include "drivers/barometer/barometer_virtual.h"
51 #include "flight/imu.h"
53 #include "config/feature.h"
54 #include "config/config.h"
55 #include "config/config_streamer.h"
56 #include "config/config_streamer_impl.h"
57 #include "config/config_eeprom_impl.h"
59 #include "scheduler/scheduler.h"
65 #include "rx/spektrum.h"
68 #include "io/gps_virtual.h"
73 uint32_t SystemCoreClock
;
75 static fdm_packet fdmPkt
;
76 static rc_packet rcPkt
;
77 static servo_packet pwmPkt
;
78 static servo_packet_raw pwmRawPkt
;
80 static bool rc_received
= false;
81 static bool fdm_received
= false;
83 static struct timespec start_time
;
84 static double simRate
= 1.0;
85 static pthread_t tcpWorker
, udpWorker
, udpWorkerRC
;
86 static bool workerRunning
= true;
87 static udpLink_t stateLink
, pwmLink
, pwmRawLink
, rcLink
;
88 static pthread_mutex_t updateLock
;
89 static pthread_mutex_t mainLoopLock
;
90 static char simulator_ip
[32] = "127.0.0.1";
92 #define PORT_PWM_RAW 9001 // Out
93 #define PORT_PWM 9002 // Out
94 #define PORT_STATE 9003 // In
95 #define PORT_RC 9004 // In
97 int targetParseArgs(int argc
, char * argv
[])
99 //The first argument should be target IP.
101 strcpy(simulator_ip
, argv
[1]);
104 printf("[SITL] The SITL will output to IP %s:%d (Gazebo) and %s:%d (RealFlightBridge)\n",
105 simulator_ip
, PORT_PWM
, simulator_ip
, PORT_PWM_RAW
);
109 int timeval_sub(struct timespec
*result
, struct timespec
*x
, struct timespec
*y
);
111 int lockMainPID(void)
113 return pthread_mutex_trylock(&mainLoopLock
);
116 #define RAD2DEG (180.0 / M_PI)
117 #define ACC_SCALE (256 / 9.80665)
118 #define GYRO_SCALE (16.4)
120 static void sendMotorUpdate(void)
122 udpSend(&pwmLink
, &pwmPkt
, sizeof(servo_packet
));
125 static void updateState(const fdm_packet
* pkt
)
127 static double last_timestamp
= 0; // in seconds
128 static uint64_t last_realtime
= 0; // in uS
129 static struct timespec last_ts
; // last packet
131 struct timespec now_ts
;
132 clock_gettime(CLOCK_MONOTONIC
, &now_ts
);
134 const uint64_t realtime_now
= micros64_real();
135 if (realtime_now
> last_realtime
+ 500*1e3
) { // 500ms timeout
136 last_timestamp
= pkt
->timestamp
;
137 last_realtime
= realtime_now
;
142 const double deltaSim
= pkt
->timestamp
- last_timestamp
; // in seconds
143 if (deltaSim
< 0) { // don't use old packet
148 x
= constrain(-pkt
->imu_linear_acceleration_xyz
[0] * ACC_SCALE
, -32767, 32767);
149 y
= constrain(-pkt
->imu_linear_acceleration_xyz
[1] * ACC_SCALE
, -32767, 32767);
150 z
= constrain(-pkt
->imu_linear_acceleration_xyz
[2] * ACC_SCALE
, -32767, 32767);
151 virtualAccSet(virtualAccDev
, x
, y
, z
);
152 // printf("[acc]%lf,%lf,%lf\n", pkt->imu_linear_acceleration_xyz[0], pkt->imu_linear_acceleration_xyz[1], pkt->imu_linear_acceleration_xyz[2]);
154 x
= constrain(pkt
->imu_angular_velocity_rpy
[0] * GYRO_SCALE
* RAD2DEG
, -32767, 32767);
155 y
= constrain(-pkt
->imu_angular_velocity_rpy
[1] * GYRO_SCALE
* RAD2DEG
, -32767, 32767);
156 z
= constrain(-pkt
->imu_angular_velocity_rpy
[2] * GYRO_SCALE
* RAD2DEG
, -32767, 32767);
157 virtualGyroSet(virtualGyroDev
, x
, y
, z
);
158 // printf("[gyr]%lf,%lf,%lf\n", pkt->imu_angular_velocity_rpy[0], pkt->imu_angular_velocity_rpy[1], pkt->imu_angular_velocity_rpy[2]);
160 // temperature in 0.01 C = 25 deg
161 virtualBaroSet(pkt
->pressure
, 2500);
162 #if !defined(USE_IMU_CALC)
163 #if defined(SET_IMU_FROM_EULER)
165 double qw
= pkt
->imu_orientation_quat
[0];
166 double qx
= pkt
->imu_orientation_quat
[1];
167 double qy
= pkt
->imu_orientation_quat
[2];
168 double qz
= pkt
->imu_orientation_quat
[3];
169 double ysqr
= qy
* qy
;
172 // roll (x-axis rotation)
173 double t0
= +2.0 * (qw
* qx
+ qy
* qz
);
174 double t1
= +1.0 - 2.0 * (qx
* qx
+ ysqr
);
175 xf
= atan2(t0
, t1
) * RAD2DEG
;
177 // pitch (y-axis rotation)
178 double t2
= +2.0 * (qw
* qy
- qz
* qx
);
179 t2
= t2
> 1.0 ? 1.0 : t2
;
180 t2
= t2
< -1.0 ? -1.0 : t2
;
181 yf
= asin(t2
) * RAD2DEG
; // from wiki
183 // yaw (z-axis rotation)
184 double t3
= +2.0 * (qw
* qz
+ qx
* qy
);
185 double t4
= +1.0 - 2.0 * (ysqr
+ qz
* qz
);
186 zf
= atan2(t3
, t4
) * RAD2DEG
;
187 imuSetAttitudeRPY(xf
, -yf
, zf
); // yes! pitch was inverted!!
189 imuSetAttitudeQuat(pkt
->imu_orientation_quat
[0], pkt
->imu_orientation_quat
[1], pkt
->imu_orientation_quat
[2], pkt
->imu_orientation_quat
[3]);
193 #if defined(USE_VIRTUAL_GPS)
194 const double longitude
= pkt
->position_xyz
[0];
195 const double latitude
= pkt
->position_xyz
[1];
196 const double altitude
= pkt
->position_xyz
[2];
197 const double speed
= sqrt(sq(pkt
->velocity_xyz
[0]) + sq(pkt
->velocity_xyz
[1]));
198 const double speed3D
= sqrt(sq(pkt
->velocity_xyz
[0]) + sq(pkt
->velocity_xyz
[1]) + sq(pkt
->velocity_xyz
[2]));
199 double course
= atan2(pkt
->velocity_xyz
[0], pkt
->velocity_xyz
[1]) * RAD2DEG
;
203 setVirtualGPS(latitude
, longitude
, altitude
, speed
, speed3D
, course
);
206 #if defined(SIMULATOR_IMU_SYNC)
207 imuSetHasNewData(deltaSim
*1e6
);
208 imuUpdateAttitude(micros());
211 if (deltaSim
< 0.02 && deltaSim
> 0) { // simulator should run faster than 50Hz
212 // simRate = simRate * 0.5 + (1e6 * deltaSim / (realtime_now - last_realtime)) * 0.5;
213 struct timespec out_ts
;
214 timeval_sub(&out_ts
, &now_ts
, &last_ts
);
215 simRate
= deltaSim
/ (out_ts
.tv_sec
+ 1e-9*out_ts
.tv_nsec
);
217 // printf("simRate = %lf, millis64 = %lu, millis64_real = %lu, deltaSim = %lf\n", simRate, millis64(), millis64_real(), deltaSim*1e6);
219 last_timestamp
= pkt
->timestamp
;
220 last_realtime
= micros64_real();
222 last_ts
.tv_sec
= now_ts
.tv_sec
;
223 last_ts
.tv_nsec
= now_ts
.tv_nsec
;
225 pthread_mutex_unlock(&updateLock
); // can send PWM output now
227 #if defined(SIMULATOR_GYROPID_SYNC)
228 pthread_mutex_unlock(&mainLoopLock
); // can run main loop
232 static void* udpThread(void* data
)
237 while (workerRunning
) {
238 n
= udpRecv(&stateLink
, &fdmPkt
, sizeof(fdm_packet
), 100);
239 if (n
== sizeof(fdm_packet
)) {
241 printf("[SITL] new fdm %d t:%f from %s:%d\n", n
, fdmPkt
.timestamp
, inet_ntoa(stateLink
.recv
.sin_addr
), stateLink
.recv
.sin_port
);
244 updateState(&fdmPkt
);
248 printf("udpThread end!!\n");
252 static float readRCSITL(const rxRuntimeState_t
*rxRuntimeState
, uint8_t channel
)
254 UNUSED(rxRuntimeState
);
255 return rcPkt
.channels
[channel
];
258 static uint8_t rxRCFrameStatus(rxRuntimeState_t
*rxRuntimeState
)
260 UNUSED(rxRuntimeState
);
261 return RX_FRAME_COMPLETE
;
264 static void *udpRCThread(void *data
)
269 while (workerRunning
) {
270 n
= udpRecv(&rcLink
, &rcPkt
, sizeof(rc_packet
), 100);
271 if (n
== sizeof(rc_packet
)) {
273 printf("[SITL] new rc %d: t:%f AETR: %d %d %d %d AUX1-4: %d %d %d %d\n", n
, rcPkt
.timestamp
,
274 rcPkt
.channels
[0], rcPkt
.channels
[1],rcPkt
.channels
[2],rcPkt
.channels
[3],
275 rcPkt
.channels
[4], rcPkt
.channels
[5],rcPkt
.channels
[6],rcPkt
.channels
[7]);
277 rxRuntimeState
.channelCount
= SIMULATOR_MAX_RC_CHANNELS
;
278 rxRuntimeState
.rcReadRawFn
= readRCSITL
;
279 rxRuntimeState
.rcFrameStatusFn
= rxRCFrameStatus
;
281 rxRuntimeState
.rxProvider
= RX_PROVIDER_UDP
;
287 printf("udpRCThread end!!\n");
291 static void* tcpThread(void* data
)
296 dyad_setTickInterval(0.2f
);
297 dyad_setUpdateTimeout(0.01f
);
299 while (workerRunning
) {
304 printf("tcpThread end!!\n");
309 void systemInit(void)
313 clock_gettime(CLOCK_MONOTONIC
, &start_time
);
314 printf("[system]Init...\n");
316 SystemCoreClock
= 500 * 1e6
; // virtual 500MHz
318 if (pthread_mutex_init(&updateLock
, NULL
) != 0) {
319 printf("Create updateLock error!\n");
323 if (pthread_mutex_init(&mainLoopLock
, NULL
) != 0) {
324 printf("Create mainLoopLock error!\n");
328 ret
= pthread_create(&tcpWorker
, NULL
, tcpThread
, NULL
);
330 printf("Create tcpWorker error!\n");
334 ret
= udpInit(&pwmLink
, simulator_ip
, PORT_PWM
, false);
335 printf("[SITL] init PwmOut UDP link to gazebo %s:%d...%d\n", simulator_ip
, PORT_PWM
, ret
);
337 ret
= udpInit(&pwmRawLink
, simulator_ip
, PORT_PWM_RAW
, false);
338 printf("[SITL] init PwmOut UDP link to RF9 %s:%d...%d\n", simulator_ip
, PORT_PWM_RAW
, ret
);
340 ret
= udpInit(&stateLink
, NULL
, PORT_STATE
, true);
341 printf("[SITL] start UDP server @%d...%d\n", PORT_STATE
, ret
);
343 ret
= udpInit(&rcLink
, NULL
, PORT_RC
, true);
344 printf("[SITL] start UDP server for RC input @%d...%d\n", PORT_RC
, ret
);
346 ret
= pthread_create(&udpWorker
, NULL
, udpThread
, NULL
);
348 printf("Create udpWorker error!\n");
352 ret
= pthread_create(&udpWorkerRC
, NULL
, udpRCThread
, NULL
);
354 printf("Create udpRCThread error!\n");
359 void systemReset(void)
361 printf("[system]Reset!\n");
362 workerRunning
= false;
363 pthread_join(tcpWorker
, NULL
);
364 pthread_join(udpWorker
, NULL
);
367 void systemResetToBootloader(bootloaderRequestType_e requestType
)
371 printf("[system]ResetToBootloader!\n");
372 workerRunning
= false;
373 pthread_join(tcpWorker
, NULL
);
374 pthread_join(udpWorker
, NULL
);
380 printf("[timer]Init...\n");
383 void failureMode(failureMode_e mode
)
385 printf("[failureMode]!!! %d\n", mode
);
389 void indicateFailure(failureMode_e mode
, int repeatCount
)
392 printf("Failure LED flash for: [failureMode]!!! %d\n", mode
);
397 uint64_t nanos64_real(void)
400 clock_gettime(CLOCK_MONOTONIC
, &ts
);
401 return (ts
.tv_sec
*1e9
+ ts
.tv_nsec
) - (start_time
.tv_sec
*1e9
+ start_time
.tv_nsec
);
404 uint64_t micros64_real(void)
407 clock_gettime(CLOCK_MONOTONIC
, &ts
);
408 return 1.0e6
*((ts
.tv_sec
+ (ts
.tv_nsec
*1.0e-9)) - (start_time
.tv_sec
+ (start_time
.tv_nsec
*1.0e-9)));
411 uint64_t millis64_real(void)
414 clock_gettime(CLOCK_MONOTONIC
, &ts
);
415 return 1.0e3
*((ts
.tv_sec
+ (ts
.tv_nsec
*1.0e-9)) - (start_time
.tv_sec
+ (start_time
.tv_nsec
*1.0e-9)));
418 uint64_t micros64(void)
420 static uint64_t last
= 0;
421 static uint64_t out
= 0;
422 uint64_t now
= nanos64_real();
424 out
+= (now
- last
) * simRate
;
428 // return micros64_real();
431 uint64_t millis64(void)
433 static uint64_t last
= 0;
434 static uint64_t out
= 0;
435 uint64_t now
= nanos64_real();
437 out
+= (now
- last
) * simRate
;
441 // return millis64_real();
444 uint32_t micros(void)
446 return micros64() & 0xFFFFFFFF;
449 uint32_t millis(void)
451 return millis64() & 0xFFFFFFFF;
454 int32_t clockCyclesToMicros(int32_t clockCycles
)
459 int32_t clockCyclesTo10thMicros(int32_t clockCycles
)
464 int32_t clockCyclesTo100thMicros(int32_t clockCycles
)
469 uint32_t clockMicrosToCycles(uint32_t micros
)
474 uint32_t getCycleCounter(void)
476 return (uint32_t) (micros64() & 0xFFFFFFFF);
479 static void microsleep(uint32_t usec
)
483 ts
.tv_nsec
= usec
*1000UL;
484 while (nanosleep(&ts
, &ts
) == -1 && errno
== EINTR
) ;
487 void delayMicroseconds(uint32_t us
)
489 microsleep(us
/ simRate
);
492 void delayMicroseconds_real(uint32_t us
)
497 void delay(uint32_t ms
)
499 uint64_t start
= millis64();
501 while ((millis64() - start
) < ms
) {
506 // Subtract the ‘struct timespec’ values X and Y, storing the result in RESULT.
507 // Return 1 if the difference is negative, otherwise 0.
509 // from: http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html
510 int timeval_sub(struct timespec
*result
, struct timespec
*x
, struct timespec
*y
)
512 unsigned int s_carry
= 0;
513 unsigned int ns_carry
= 0;
514 // Perform the carry for the later subtraction by updating y.
515 if (x
->tv_nsec
< y
->tv_nsec
) {
516 int nsec
= (y
->tv_nsec
- x
->tv_nsec
) / 1000000000 + 1;
517 ns_carry
+= 1000000000 * nsec
;
521 // Compute the time remaining to wait. tv_usec is certainly positive.
522 result
->tv_sec
= x
->tv_sec
- y
->tv_sec
- s_carry
;
523 result
->tv_nsec
= x
->tv_nsec
- y
->tv_nsec
+ ns_carry
;
525 // Return 1 if result is negative.
526 return x
->tv_sec
< y
->tv_sec
;
530 static pwmOutputPort_t servos
[MAX_SUPPORTED_SERVOS
];
532 // real value to send
533 static int16_t motorsPwm
[MAX_SUPPORTED_MOTORS
];
534 static int16_t servosPwm
[MAX_SUPPORTED_SERVOS
];
535 static int16_t idlePulse
;
537 void servoDevInit(const servoDevConfig_t
*servoConfig
)
539 printf("[SITL] Init servos num %d rate %d center %d\n", MAX_SUPPORTED_SERVOS
,
540 servoConfig
->servoPwmRate
, servoConfig
->servoCenterPulse
);
541 for (uint8_t servoIndex
= 0; servoIndex
< MAX_SUPPORTED_SERVOS
; servoIndex
++) {
542 servos
[servoIndex
].enabled
= true;
546 static motorDevice_t pwmMotorDevice
; // Forward
548 pwmOutputPort_t
*pwmGetMotors(void)
553 static float pwmConvertFromExternal(uint16_t externalValue
)
555 return (float)externalValue
;
558 static uint16_t pwmConvertToExternal(float motorValue
)
560 return (uint16_t)motorValue
;
563 static void pwmDisableMotors(void)
565 pwmMotorDevice
.enabled
= false;
568 static bool pwmEnableMotors(void)
570 pwmMotorDevice
.enabled
= true;
575 static void pwmWriteMotor(uint8_t index
, float value
)
577 if (pthread_mutex_trylock(&updateLock
) != 0) return;
579 if (index
< MAX_SUPPORTED_MOTORS
) {
580 motorsPwm
[index
] = value
- idlePulse
;
583 if (index
< pwmRawPkt
.motorCount
) {
584 pwmRawPkt
.pwm_output_raw
[index
] = value
;
587 pthread_mutex_unlock(&updateLock
); // can send PWM output now
590 static void pwmWriteMotorInt(uint8_t index
, uint16_t value
)
592 pwmWriteMotor(index
, (float)value
);
595 static void pwmShutdownPulsesForAllMotors(void)
597 pwmMotorDevice
.enabled
= false;
600 static bool pwmIsMotorEnabled(unsigned index
)
602 return motors
[index
].enabled
;
605 static void pwmCompleteMotorUpdate(void)
608 // for gazebo8 ArduCopterPlugin remap, normal range = [0.0, 1.0], 3D rang = [-1.0, 1.0]
610 double outScale
= 1000.0;
611 if (featureIsEnabled(FEATURE_3D
)) {
615 pwmPkt
.motor_speed
[3] = motorsPwm
[0] / outScale
;
616 pwmPkt
.motor_speed
[0] = motorsPwm
[1] / outScale
;
617 pwmPkt
.motor_speed
[1] = motorsPwm
[2] / outScale
;
618 pwmPkt
.motor_speed
[2] = motorsPwm
[3] / outScale
;
620 // get one "fdm_packet" can only send one "servo_packet"!!
621 if (pthread_mutex_trylock(&updateLock
) != 0) return;
622 udpSend(&pwmLink
, &pwmPkt
, sizeof(servo_packet
));
623 // printf("[pwm]%u:%u,%u,%u,%u\n", idlePulse, motorsPwm[0], motorsPwm[1], motorsPwm[2], motorsPwm[3]);
624 udpSend(&pwmRawLink
, &pwmRawPkt
, sizeof(servo_packet_raw
));
627 void pwmWriteServo(uint8_t index
, float value
)
629 servosPwm
[index
] = value
;
630 if (index
+ pwmRawPkt
.motorCount
< SIMULATOR_MAX_PWM_CHANNELS
) {
631 // In pwmRawPkt, we put servo right after the motors.
632 pwmRawPkt
.pwm_output_raw
[index
+ pwmRawPkt
.motorCount
] = value
;
636 static const motorVTable_t vTable
= {
637 .postInit
= motorPostInitNull
,
638 .convertExternalToMotor
= pwmConvertFromExternal
,
639 .convertMotorToExternal
= pwmConvertToExternal
,
640 .enable
= pwmEnableMotors
,
641 .disable
= pwmDisableMotors
,
642 .isMotorEnabled
= pwmIsMotorEnabled
,
643 .decodeTelemetry
= motorDecodeTelemetryNull
,
644 .write
= pwmWriteMotor
,
645 .writeInt
= pwmWriteMotorInt
,
646 .updateComplete
= pwmCompleteMotorUpdate
,
647 .shutdown
= pwmShutdownPulsesForAllMotors
,
648 .requestTelemetry
= NULL
,
653 bool motorPwmDevInit(motorDevice_t
*device
, const motorDevConfig_t
*motorConfig
, uint16_t _idlePulse
)
660 device
->vTable
= &vTable
;
661 const uint8_t motorCount
= device
->count
;
662 printf("Initialized motor count %d\n", motorCount
);
663 pwmRawPkt
.motorCount
= motorCount
;
665 idlePulse
= _idlePulse
;
667 for (int motorIndex
= 0; motorIndex
< MAX_SUPPORTED_MOTORS
&& motorIndex
< motorCount
; motorIndex
++) {
668 motors
[motorIndex
].enabled
= true;
675 uint16_t adcGetChannel(uint8_t channel
)
683 char _Min_Stack_Size
;
686 static FILE *eepromFd
= NULL
;
688 bool loadEEPROMFromFile(void)
690 if (eepromFd
!= NULL
) {
691 fprintf(stderr
, "[FLASH_Unlock] eepromFd != NULL\n");
696 eepromFd
= fopen(EEPROM_FILENAME
, "r+");
697 if (eepromFd
!= NULL
) {
699 fseek(eepromFd
, 0, SEEK_END
);
700 size_t lSize
= ftell(eepromFd
);
703 size_t n
= fread(eepromData
, 1, sizeof(eepromData
), eepromFd
);
705 printf("[FLASH_Unlock] loaded '%s', size = %ld / %ld\n", EEPROM_FILENAME
, lSize
, sizeof(eepromData
));
707 fprintf(stderr
, "[FLASH_Unlock] failed to load '%s'\n", EEPROM_FILENAME
);
711 printf("[FLASH_Unlock] created '%s', size = %ld\n", EEPROM_FILENAME
, sizeof(eepromData
));
712 if ((eepromFd
= fopen(EEPROM_FILENAME
, "w+")) == NULL
) {
713 fprintf(stderr
, "[FLASH_Unlock] failed to create '%s'\n", EEPROM_FILENAME
);
717 if (fwrite(eepromData
, sizeof(eepromData
), 1, eepromFd
) != 1) {
718 fprintf(stderr
, "[FLASH_Unlock] write failed: %s\n", strerror(errno
));
725 void configUnlock(void)
727 loadEEPROMFromFile();
730 void configLock(void)
733 if (eepromFd
!= NULL
) {
734 fseek(eepromFd
, 0, SEEK_SET
);
735 fwrite(eepromData
, 1, sizeof(eepromData
), eepromFd
);
738 printf("[FLASH_Lock] saved '%s'\n", EEPROM_FILENAME
);
740 fprintf(stderr
, "[FLASH_Lock] eeprom is not unlocked\n");
744 configStreamerResult_e
configWriteWord(uintptr_t address
, config_streamer_buffer_type_t
*buffer
)
746 STATIC_ASSERT(CONFIG_STREAMER_BUFFER_SIZE
== sizeof(uint32_t), "CONFIG_STREAMER_BUFFER_SIZE does not match written size");
748 if ((address
>= (uintptr_t)eepromData
) && (address
+ sizeof(uint32_t) <= (uintptr_t)ARRAYEND(eepromData
))) {
749 memcpy((void*)address
, buffer
, sizeof(config_streamer_buffer_type_t
));
750 printf("[FLASH_ProgramWord]%p = %08x\n", (void*)address
, *((uint32_t*)address
));
752 printf("[FLASH_ProgramWord]%p out of range!\n", (void*)address
);
754 return CONFIG_RESULT_SUCCESS
;
757 void IOConfigGPIO(IO_t io
, ioConfig_t cfg
)
761 printf("IOConfigGPIO\n");
764 void spektrumBind(rxConfig_t
*rxConfig
)
767 printf("spektrumBind\n");
772 printf("debugInit\n");
775 void unusedPinsInit(void)
777 printf("unusedPinsInit\n");
790 void IOInitGlobal(void)
795 IO_t
IOGetByTag(ioTag_t tag
)
801 const mcuTypeInfo_t
*getMcuTypeInfo(void)
803 static const mcuTypeInfo_t info
= { .id
= MCU_TYPE_SIMULATOR
, .name
= "SIMULATOR" };