mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-03 12:48:53 +08:00
Add a test hook for the PX4IO interface. Wire up some simple tests for the serial interface.
Signal quality looks good at 1.5Mbps. Transmit timing is ~450µs per packet, ~20µs packet-to-packet delay spinning in a loop transmitting.
This commit is contained in:
@@ -72,7 +72,14 @@ public:
|
||||
* @return Zero on success.
|
||||
*/
|
||||
virtual int get_reg(uint8_t page, uint8_t offset, uint16_t *values, unsigned num_values) = 0;
|
||||
|
||||
/**
|
||||
* Perform an interface test.
|
||||
*
|
||||
* @param mode Optional test mode.
|
||||
*/
|
||||
virtual int test(unsigned mode) = 0;
|
||||
};
|
||||
|
||||
extern PX4IO_interface *io_i2c_interface(int bus, uint8_t address);
|
||||
extern PX4IO_interface *io_serial_interface(int port);
|
||||
extern PX4IO_interface *io_serial_interface();
|
||||
|
||||
@@ -69,6 +69,8 @@ public:
|
||||
|
||||
virtual bool ok();
|
||||
|
||||
virtual int test(unsigned mode);
|
||||
|
||||
private:
|
||||
static const unsigned _retries = 2;
|
||||
|
||||
@@ -107,6 +109,12 @@ PX4IO_I2C::ok()
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
PX4IO_I2C::test(unsigned mode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
PX4IO_I2C::set_reg(uint8_t page, uint8_t offset, const uint16_t *values, unsigned num_values)
|
||||
{
|
||||
|
||||
@@ -87,6 +87,7 @@ public:
|
||||
virtual int get_reg(uint8_t page, uint8_t offset, uint16_t *values, unsigned num_values);
|
||||
|
||||
virtual bool ok();
|
||||
virtual int test(unsigned mode);
|
||||
|
||||
private:
|
||||
/*
|
||||
@@ -102,10 +103,7 @@ private:
|
||||
*/
|
||||
static IOPacket _dma_buffer; // XXX static to ensure DMA-able memory
|
||||
|
||||
static const unsigned _serial_tx_dma = PX4IO_SERIAL_RX_DMAMAP;
|
||||
DMA_HANDLE _tx_dma;
|
||||
|
||||
static const unsigned _serial_rx_dma = PX4IO_SERIAL_TX_DMAMAP;
|
||||
DMA_HANDLE _rx_dma;
|
||||
|
||||
/** set if we have started a transaction that expects a reply */
|
||||
@@ -164,7 +162,7 @@ private:
|
||||
|
||||
IOPacket PX4IO_serial::_dma_buffer;
|
||||
|
||||
PX4IO_interface *io_serial_interface(int port)
|
||||
PX4IO_interface *io_serial_interface()
|
||||
{
|
||||
return new PX4IO_serial();
|
||||
}
|
||||
@@ -175,8 +173,8 @@ PX4IO_serial::PX4IO_serial() :
|
||||
_expect_reply(false)
|
||||
{
|
||||
/* allocate DMA */
|
||||
_tx_dma = stm32_dmachannel(_serial_tx_dma);
|
||||
_rx_dma = stm32_dmachannel(_serial_rx_dma);
|
||||
_tx_dma = stm32_dmachannel(PX4IO_SERIAL_TX_DMAMAP);
|
||||
_rx_dma = stm32_dmachannel(PX4IO_SERIAL_RX_DMAMAP);
|
||||
if ((_tx_dma == nullptr) || (_rx_dma == nullptr))
|
||||
return;
|
||||
|
||||
@@ -209,14 +207,25 @@ PX4IO_serial::PX4IO_serial() :
|
||||
|
||||
PX4IO_serial::~PX4IO_serial()
|
||||
{
|
||||
if (_tx_dma != nullptr)
|
||||
if (_tx_dma != nullptr) {
|
||||
stm32_dmastop(_tx_dma);
|
||||
stm32_dmafree(_tx_dma);
|
||||
if (_rx_dma != nullptr)
|
||||
}
|
||||
if (_rx_dma != nullptr) {
|
||||
stm32_dmastop(_rx_dma);
|
||||
stm32_dmafree(_rx_dma);
|
||||
}
|
||||
|
||||
/* reset the UART */
|
||||
_CR1(0);
|
||||
_CR2(0);
|
||||
_CR3(0);
|
||||
|
||||
/* restore the GPIOs */
|
||||
stm32_unconfiggpio(PX4IO_SERIAL_TX_GPIO);
|
||||
stm32_unconfiggpio(PX4IO_SERIAL_RX_GPIO);
|
||||
|
||||
/* and kill our semaphores */
|
||||
sem_destroy(&_completion_semaphore);
|
||||
sem_destroy(&_bus_semaphore);
|
||||
}
|
||||
@@ -232,6 +241,40 @@ PX4IO_serial::ok()
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
PX4IO_serial::test(unsigned mode)
|
||||
{
|
||||
|
||||
switch (mode) {
|
||||
case 0:
|
||||
lowsyslog("test 0\n");
|
||||
|
||||
/* kill DMA, this is a PIO test */
|
||||
stm32_dmastop(_tx_dma);
|
||||
stm32_dmastop(_rx_dma);
|
||||
_CR3(_CR3() & ~(USART_CR3_DMAR | USART_CR3_DMAT));
|
||||
|
||||
for (;;) {
|
||||
while (!(_SR() & USART_SR_TXE))
|
||||
;
|
||||
_DR(0x55);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case 1:
|
||||
lowsyslog("test 1\n");
|
||||
{
|
||||
uint16_t value = 0x5555;
|
||||
for (;;) {
|
||||
if (set_reg(0x55, 0x55, &value, 1) != OK)
|
||||
return -2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
PX4IO_serial::set_reg(uint8_t page, uint8_t offset, const uint16_t *values, unsigned num_values)
|
||||
{
|
||||
@@ -297,16 +340,20 @@ PX4IO_serial::_wait_complete(bool expect_reply)
|
||||
stm32_dmastart(_rx_dma, _dma_callback, this, false);
|
||||
|
||||
/* start TX DMA - no callback if we also expect a reply */
|
||||
stm32_dmastart(_tx_dma, _dma_callback, expect_reply ? nullptr : this, false);
|
||||
stm32_dmastart(_tx_dma, /*expect_reply ? nullptr :*/ _dma_callback, this, false);
|
||||
|
||||
/* compute the deadline for a 5ms timeout */
|
||||
struct timespec abstime;
|
||||
clock_gettime(CLOCK_REALTIME, &abstime);
|
||||
#if 1
|
||||
abstime.tv_sec++;
|
||||
#else
|
||||
abstime.tv_nsec += 5000000; /* 5ms timeout */
|
||||
while (abstime.tv_nsec > 1000000000) {
|
||||
abstime.tv_sec++;
|
||||
abstime.tv_nsec -= 1000000000;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* wait for the transaction to complete - 64 bytes @ 1.5Mbps ~426µs */
|
||||
int ret;
|
||||
@@ -316,11 +363,13 @@ PX4IO_serial::_wait_complete(bool expect_reply)
|
||||
if (ret == OK)
|
||||
break;
|
||||
|
||||
if (ret == ETIMEDOUT) {
|
||||
if (errno == ETIMEDOUT) {
|
||||
lowsyslog("timeout waiting for PX4IO link (%d/%d)\n", stm32_dmaresidual(_tx_dma), stm32_dmaresidual(_rx_dma));
|
||||
/* something has broken - clear out any partial DMA state and reconfigure */
|
||||
_reset_dma();
|
||||
break;
|
||||
}
|
||||
lowsyslog("unexpected ret %d/%d\n", ret, errno);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
+150
-132
@@ -104,8 +104,8 @@ public:
|
||||
/**
|
||||
* Set the update rate for actuator outputs from FMU to IO.
|
||||
*
|
||||
* @param rate The rate in Hz actuator outpus are sent to IO.
|
||||
* Min 10 Hz, max 400 Hz
|
||||
* @param rate The rate in Hz actuator outpus are sent to IO.
|
||||
* Min 10 Hz, max 400 Hz
|
||||
*/
|
||||
int set_update_rate(int rate);
|
||||
|
||||
@@ -120,14 +120,14 @@ public:
|
||||
/**
|
||||
* Push failsafe values to IO.
|
||||
*
|
||||
* @param vals Failsafe control inputs: in us PPM (900 for zero, 1500 for centered, 2100 for full)
|
||||
* @param len Number of channels, could up to 8
|
||||
* @param vals Failsafe control inputs: in us PPM (900 for zero, 1500 for centered, 2100 for full)
|
||||
* @param len Number of channels, could up to 8
|
||||
*/
|
||||
int set_failsafe_values(const uint16_t *vals, unsigned len);
|
||||
|
||||
/**
|
||||
* Print the current status of IO
|
||||
*/
|
||||
* Print the current status of IO
|
||||
*/
|
||||
void print_status();
|
||||
|
||||
private:
|
||||
@@ -1579,7 +1579,7 @@ start(int argc, char *argv[])
|
||||
PX4IO_interface *interface;
|
||||
|
||||
#if defined(CONFIG_ARCH_BOARD_PX4FMUV2)
|
||||
interface = io_serial_interface(5); /* XXX wrong port! */
|
||||
interface = io_serial_interface();
|
||||
#elif defined(CONFIG_ARCH_BOARD_PX4FMU)
|
||||
interface = io_i2c_interface(PX4_I2C_BUS_ONBOARD, PX4_I2C_OBDEV_PX4IO);
|
||||
#else
|
||||
@@ -1710,6 +1710,35 @@ monitor(void)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
if_test(unsigned mode)
|
||||
{
|
||||
PX4IO_interface *interface;
|
||||
|
||||
#if defined(CONFIG_ARCH_BOARD_PX4FMUV2)
|
||||
interface = io_serial_interface();
|
||||
#elif defined(CONFIG_ARCH_BOARD_PX4FMU)
|
||||
interface = io_i2c_interface(PX4_I2C_BUS_ONBOARD, PX4_I2C_OBDEV_PX4IO);
|
||||
#else
|
||||
# error Unknown board - cannot select interface.
|
||||
#endif
|
||||
|
||||
if (interface == nullptr)
|
||||
errx(1, "cannot alloc interface");
|
||||
|
||||
if (!interface->ok()) {
|
||||
delete interface;
|
||||
errx(1, "interface init failed");
|
||||
} else {
|
||||
|
||||
int result = interface->test(mode);
|
||||
delete interface;
|
||||
errx(0, "test returned %d", result);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
@@ -1722,130 +1751,6 @@ px4io_main(int argc, char *argv[])
|
||||
if (!strcmp(argv[1], "start"))
|
||||
start(argc - 1, argv + 1);
|
||||
|
||||
if (!strcmp(argv[1], "limit")) {
|
||||
|
||||
if (g_dev != nullptr) {
|
||||
|
||||
if ((argc > 2)) {
|
||||
g_dev->set_update_rate(atoi(argv[2]));
|
||||
} else {
|
||||
errx(1, "missing argument (50 - 200 Hz)");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "current")) {
|
||||
if (g_dev != nullptr) {
|
||||
if ((argc > 3)) {
|
||||
g_dev->set_battery_current_scaling(atof(argv[2]), atof(argv[3]));
|
||||
} else {
|
||||
errx(1, "missing argument (apm_per_volt, amp_offset)");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "failsafe")) {
|
||||
|
||||
if (argc < 3) {
|
||||
errx(1, "failsafe command needs at least one channel value (ppm)");
|
||||
}
|
||||
|
||||
if (g_dev != nullptr) {
|
||||
|
||||
/* set values for first 8 channels, fill unassigned channels with 1500. */
|
||||
uint16_t failsafe[8];
|
||||
|
||||
for (int i = 0; i < sizeof(failsafe) / sizeof(failsafe[0]); i++)
|
||||
{
|
||||
/* set channel to commanline argument or to 900 for non-provided channels */
|
||||
if (argc > i + 2) {
|
||||
failsafe[i] = atoi(argv[i+2]);
|
||||
if (failsafe[i] < 800 || failsafe[i] > 2200) {
|
||||
errx(1, "value out of range of 800 < value < 2200. Aborting.");
|
||||
}
|
||||
} else {
|
||||
/* a zero value will result in stopping to output any pulse */
|
||||
failsafe[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int ret = g_dev->set_failsafe_values(failsafe, sizeof(failsafe) / sizeof(failsafe[0]));
|
||||
|
||||
if (ret != OK)
|
||||
errx(ret, "failed setting failsafe values");
|
||||
} else {
|
||||
errx(1, "not loaded");
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "recovery")) {
|
||||
|
||||
if (g_dev != nullptr) {
|
||||
/*
|
||||
* Enable in-air restart support.
|
||||
* We can cheat and call the driver directly, as it
|
||||
* doesn't reference filp in ioctl()
|
||||
*/
|
||||
g_dev->ioctl(NULL, PX4IO_INAIR_RESTART_ENABLE, 1);
|
||||
} else {
|
||||
errx(1, "not loaded");
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "stop")) {
|
||||
|
||||
if (g_dev != nullptr) {
|
||||
/* stop the driver */
|
||||
delete g_dev;
|
||||
} else {
|
||||
errx(1, "not loaded");
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
if (!strcmp(argv[1], "status")) {
|
||||
|
||||
if (g_dev != nullptr) {
|
||||
printf("[px4io] loaded\n");
|
||||
g_dev->print_status();
|
||||
} else {
|
||||
printf("[px4io] not loaded\n");
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "debug")) {
|
||||
if (argc <= 2) {
|
||||
printf("usage: px4io debug LEVEL\n");
|
||||
exit(1);
|
||||
}
|
||||
if (g_dev == nullptr) {
|
||||
printf("px4io is not started\n");
|
||||
exit(1);
|
||||
}
|
||||
uint8_t level = atoi(argv[2]);
|
||||
/* we can cheat and call the driver directly, as it
|
||||
* doesn't reference filp in ioctl()
|
||||
*/
|
||||
int ret = g_dev->ioctl(NULL, PX4IO_SET_DEBUG, level);
|
||||
if (ret != 0) {
|
||||
printf("SET_DEBUG failed - %d\n", ret);
|
||||
exit(1);
|
||||
}
|
||||
printf("SET_DEBUG %u OK\n", (unsigned)level);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* note, stop not currently implemented */
|
||||
|
||||
if (!strcmp(argv[1], "update")) {
|
||||
|
||||
if (g_dev != nullptr) {
|
||||
@@ -1896,6 +1801,119 @@ px4io_main(int argc, char *argv[])
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "iftest")) {
|
||||
if (g_dev != nullptr)
|
||||
errx(1, "can't iftest when started");
|
||||
|
||||
if_test((argc > 2) ? strtol(argv[2], NULL, 0) : 0);
|
||||
}
|
||||
|
||||
/* commands below here require a started driver */
|
||||
|
||||
if (g_dev == nullptr)
|
||||
errx(1, "not started");
|
||||
|
||||
if (!strcmp(argv[1], "limit")) {
|
||||
|
||||
if ((argc > 2)) {
|
||||
g_dev->set_update_rate(atoi(argv[2]));
|
||||
} else {
|
||||
errx(1, "missing argument (50 - 200 Hz)");
|
||||
return 1;
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "current")) {
|
||||
if ((argc > 3)) {
|
||||
g_dev->set_battery_current_scaling(atof(argv[2]), atof(argv[3]));
|
||||
} else {
|
||||
errx(1, "missing argument (apm_per_volt, amp_offset)");
|
||||
return 1;
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "failsafe")) {
|
||||
|
||||
if (argc < 3) {
|
||||
errx(1, "failsafe command needs at least one channel value (ppm)");
|
||||
}
|
||||
|
||||
/* set values for first 8 channels, fill unassigned channels with 1500. */
|
||||
uint16_t failsafe[8];
|
||||
|
||||
for (int i = 0; i < sizeof(failsafe) / sizeof(failsafe[0]); i++) {
|
||||
|
||||
/* set channel to commandline argument or to 900 for non-provided channels */
|
||||
if (argc > i + 2) {
|
||||
failsafe[i] = atoi(argv[i+2]);
|
||||
if (failsafe[i] < 800 || failsafe[i] > 2200) {
|
||||
errx(1, "value out of range of 800 < value < 2200. Aborting.");
|
||||
}
|
||||
} else {
|
||||
/* a zero value will result in stopping to output any pulse */
|
||||
failsafe[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int ret = g_dev->set_failsafe_values(failsafe, sizeof(failsafe) / sizeof(failsafe[0]));
|
||||
|
||||
if (ret != OK)
|
||||
errx(ret, "failed setting failsafe values");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "recovery")) {
|
||||
|
||||
/*
|
||||
* Enable in-air restart support.
|
||||
* We can cheat and call the driver directly, as it
|
||||
* doesn't reference filp in ioctl()
|
||||
*/
|
||||
g_dev->ioctl(NULL, PX4IO_INAIR_RESTART_ENABLE, 1);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "stop")) {
|
||||
|
||||
/* stop the driver */
|
||||
delete g_dev;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
if (!strcmp(argv[1], "status")) {
|
||||
|
||||
printf("[px4io] loaded\n");
|
||||
g_dev->print_status();
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "debug")) {
|
||||
if (argc <= 2) {
|
||||
printf("usage: px4io debug LEVEL\n");
|
||||
exit(1);
|
||||
}
|
||||
if (g_dev == nullptr) {
|
||||
printf("px4io is not started\n");
|
||||
exit(1);
|
||||
}
|
||||
uint8_t level = atoi(argv[2]);
|
||||
/* we can cheat and call the driver directly, as it
|
||||
* doesn't reference filp in ioctl()
|
||||
*/
|
||||
int ret = g_dev->ioctl(NULL, PX4IO_SET_DEBUG, level);
|
||||
if (ret != 0) {
|
||||
printf("SET_DEBUG failed - %d\n", ret);
|
||||
exit(1);
|
||||
}
|
||||
printf("SET_DEBUG %u OK\n", (unsigned)level);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "rx_dsm") ||
|
||||
!strcmp(argv[1], "rx_dsm_10bit") ||
|
||||
!strcmp(argv[1], "rx_dsm_11bit") ||
|
||||
@@ -1909,6 +1927,6 @@ px4io_main(int argc, char *argv[])
|
||||
if (!strcmp(argv[1], "monitor"))
|
||||
monitor();
|
||||
|
||||
out:
|
||||
out:
|
||||
errx(1, "need a command, try 'start', 'stop', 'status', 'test', 'monitor', 'debug', 'recovery', 'limit', 'current', 'failsafe' or 'update'");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user