Em-Nest

Just another blog

+menu-



    • ECAP PWM capture in Linux

      I have added LV-Maxsonar-EZ2 (ultrasound proximity sensor) support. Because Maxsonar’s RS232 output is not actual UART (signal is inverted), thus Beagle’s UART cannot be used without additional circuits, I have decided to use Maxsonar’s PWM output. But there is no support for ECAP PWM capture in Linux kernel.

      So, I had to use mmap. There are some minor issues like I do not read SYSCLK (L3_FREQ) so I do not know accurate time of ON and OFF cycle but because PWM period does not change and I am interested in ratios only I do not need it. Here are some pieces of source code to make ECAP running, I hope it helps someone:

      #include <stdio.h>
      #include <stdlib.h>
      #include <sys/mman.h>
      #include <sys/stat.h>
      #include <fcntl.h>
      
      #include <unistd.h>
      #include <errno.h>
      #include <cstdint>
      
      // register address
      static const size_t ECAP0_BASE_ADDRESS = 0x48300100;
      static const size_t ECAP2_BASE_ADDRESS = 0x48304100;
      
      // block size
      static const size_t EACP_MEM_SIZE = 0x0180;
      
      // register offsets
      static const unsigned int ECAP_TSCTR = 0x00;
      static const unsigned int ECAP_CTRPHS = 0x04;
      static const unsigned int ECAP_CAP1 = 0x08;
      static const unsigned int ECAP_CAP2 = 0x0C;
      static const unsigned int ECAP_CAP3 = 0x10;
      static const unsigned int ECAP_CAP4 = 0x14;
      static const unsigned int ECAP_ECCTL1 = 0x28;
      static const unsigned int ECAP_ECCTL2 = 0x2A;
      static const unsigned int ECAP_ECEINT = 0x2C;
      static const unsigned int ECAP_ECFLG = 0x2E;
      static const unsigned int ECAP_ECCLR = 0x30;
      static const unsigned int ECAP_ECFRC = 0x32;
      static const unsigned int ECAP_REVID = 0x5C;
      
      // CTR overflow flag (to detect PWM recepion stopped)
      static const unsigned int CTR_OVF_MASK = 0x0020;
      
      // from ARM Reference Manual
      #define EC_RISING 0x0
      #define EC_FALLING 0x1
      
      #define EC_ABS_MODE 0x0
      #define EC_DELTA_MODE 0x1
      
      #define EC_BYPASS 0x0
      #define EC_DIV1 0x0
      #define EC_DIV2 0x1
      #define EC_DIV4 0x2
      #define EC_DIV6 0x3
      #define EC_DIV8 0x4
      #define EC_DIV10 0x5
      
      #define EC_CONTINUOUS 0x0
      #define EC_ONESHOT 0x1
      
      #define EC_EVENT1 0x0
      #define EC_EVENT2 0x1
      #define EC_EVENT3 0x2
      #define EC_EVENT4 0x3
      
      #define EC_ARM 0x1
      
      #define EC_FREEZE 0x0
      #define EC_RUN 0x1
      
      #define EC_SYNCIN 0x0
      #define EC_CTR_PRD 0x1
      #define EC_SYNCO_DIS 0x2
      
      #define EC_CAP_MODE 0x0
      #define EC_APWM_MODE 0x1
      
      #define EC_ACTV_HI 0x0
      #define EC_ACTV_LO 0x1
      
      #define EC_DISABLE 0x0
      #define EC_ENABLE 0x1
      #define EC_FORCE 0x1
      
      // bit positions in registers
      #define ECCTL1_CAP1POL 0
      #define ECCTL1_CAP2POL 2
      #define ECCTL1_CAP3POL 4
      #define ECCTL1_CAP4POL 6
      #define ECCTL1_CTRRST1 1
      #define ECCTL1_CTRRST2 3
      #define ECCTL1_CTRRST3 5
      #define ECCTL1_CTRRST4 7
      #define ECCTL1_CAPLDEN 8
      #define ECCTL1_PRESCAL 9
      #define ECCTL1_FREESFT 14
      
      #define ECCTL2_CONTONE 0
      #define ECCTL2_STOPWRP 1
      #define ECCTL2_REARM   3
      #define ECCTL2_TSCSTOP 4
      #define ECCTL2_SYNCIEN 5
      #define ECCTL2_SYNCOSEL 6
      #define ECCTL2_SWSYNC  8
      #define ECCTL2_CAPAPWM 9
      #define ECCTL2_APWMPOL 10
      
      ...
      
      /*
      1. Set ECAP mux (not shown here)
      */
      
      ...
      
      /*
      2. Open /dev/mem and map register addresses
      */
      
      int fd = open("/dev/mem", O_RDWR);
      
      // set ECAP2
      long pageSize = sysconf(_SC_PAGE_SIZE);
      // get page base address
      size_t baseAddr = ECAP2_BASE_ADDRESS / (size_t)pageSize;
      baseAddr *= (size_t)pageSize;
      size_t addrOffset = ECAP2_BASE_ADDRESS - baseAddr;
      // map
      void *gpio_addr = mmap(0, EACP_MEM_SIZE + addrOffset, PROT_READ | PROT_WRITE, MAP_SHARED, fd, baseAddr);
      
      // map was successful
      if (gpio_addr != MAP_FAILED)
      {
      	gpio_addr = (void *)((unsigned char *)gpio_addr + m_addrOffset);
      }
      
      ...
      
      /*
      3. set ECAP module to continuesly detect PWM on ECAP1 and ECAP2 registers
      */
      
      *(uint16_t *)((unsigned char *)gpio_addr + ECAP_ECCTL1) = 
      	(EC_FALLING << ECCTL1_CAP1POL) | (EC_RISING << ECCTL1_CAP2POL) | (EC_FALLING << ECCTL1_CAP3POL) | (EC_RISING << ECCTL1_CAP4POL) | 
      	(EC_DELTA_MODE << ECCTL1_CTRRST1) | (EC_DELTA_MODE << ECCTL1_CTRRST2) | (EC_DELTA_MODE << ECCTL1_CTRRST3) | (EC_DELTA_MODE << ECCTL1_CTRRST4) | 
      	(EC_ENABLE << ECCTL1_CAPLDEN) | (EC_DIV1 << ECCTL1_PRESCAL) | (3 << ECCTL1_FREESFT);
      *(uint16_t *)((unsigned char *)gpio_addr + ECAP_ECCTL2) = 
      	(EC_CAP_MODE << ECCTL2_CAPAPWM) | (EC_CONTINUOUS << ECCTL2_CONTONE) | (EC_SYNCO_DIS << ECCTL2_SYNCOSEL) | (EC_DISABLE << ECCTL2_SYNCIEN) | 
      	(EC_RUN << ECCTL2_TSCSTOP) | (1 << ECCTL2_STOPWRP);
      
      ...
      
      /*
      4. any time read ECAP1 and ECAP2 to get ON and OFF duty
      */
      
      uint32_t T1 = *(uint32_t *)((unsigned char *)gpio_addr + ECAP_CAP1);    // on1
      uint32_t T2 = *(uint32_t *)((unsigned char *)gpio_addr + ECAP_CAP2);    // off1
      
      ...
      
      /*
      5. detect overflow (optional)
      */
      
      if (((*(uint16_t *)(addrChar + ECAP_ECFLG)) & CTR_OVF_MASK) != 0)
      {
      	*(uint16_t *)(addrChar + ECAP_ECCLR) |= CTR_OVF_MASK;
      	...
      }
      

    • Test flights

      The heli underwent some test flights. Unfortunately I have no videos (I cannot do two things paralelly ;) ) but at least some results are here.

      But first of all, lets show how the heli is controlled. I am using a standard USB joystick and a computer. It is not a good way to control heli outdoors but on the other hand the heli is intended to be a remote controlled UAV (one day). Thus some advanced “ground station” is vital.

      Here goes the picture:heli control

      In the future I am going to make it a bit more compact and mobile (I’ll probably use a tablet :) ).

      Anyway, when it comes to flight results, here it is.

      Manual control works fine and heli immediately reacts on pilot’s commands. But IMU/INS is surpassing some hurdless. There is a huge problem with vibrations. Main rotor causes massive high frequency vibrations. The INS board is placed in a mechanically damped platform in all 3 axes but still, measured vibrations exceed 6 g. High frequency is not such a problem as it can be filtered but another problem are low frequency vibrations around 5 Hz – heli is essentially shaking in all three axis. This can be clearly seen on the artificial horizon.

      Here are some graphs showing vibration problem:

      heli_acc takeoffThe figure shows acceleration in all 3 axis during a (slow) takeoff. It can be seen that vibrations are huge. Another problem is influence of vibrations/shaking on the gyroscopes. They are working around their limits and potentially cannot handle a sudden rotation.

      heli_gyro

      During fast rotation around yaw axis there are significant vibrations around other two axes. Gyro’s sensing limit is around 6 randians per second. Vibrations are almost 3. And again, this is for 3axial mechanical damping of the INS platform!

      heli_acc_fftHere is some brief frequency analysis of the vibrations. It can be seen that vibrations start at around 20 Hz.

      Compared to quadrocopter, helicopter is a wild beast…


    • Irregular Update

      New INS Board

      As the old board is not reliable a new board with new sensors has been designed. As MLX90609 is out of production ADXRS450 is the new choice. It is pretty expensive but has continues self test and potential failure can be detected. Besides the self test the gyro is factory precalibrated, accurate and comes also in LCC package.

      Now all I have to do is to solder the board. And make it running :)

      EFIS (AHRS)

      Finally, I have implemented TaitBryan angle conversion and added simple EFIS display to the service application.  Will be handy for semi automatic flights. Some minor things are not implemented yet but, generally, every essential information is displayed.

      EFIS

       Magnetometer Calibration Video

      Added a short video showing magnetometer calibration algorithm working real-time. See Calibration section.


    • UML Modelling Tool

      When a software grows keeping track of states and transitions becomes more and more difficult. When simple enumeration – switch – case statemachine is not feasible anymore it is time to look at some UML (statechart) modelling tool. There are tons of them around the Web but unfortunately most of them are not able to generate code for created statemachines in C or C++. If there is some, it costs a fortune like Rhapsody or when free like Quantum Leaps it brings it’s own framework (for pedants, Rhapsody has it’s framework too).

      But when you want to have full control of event flow and generation and statemachine execution. It is a problem. Finally I have found a possible solution. It is called Yakindu. After few tests it looks good enough. I’ll look how to incorporate it to the heli system.


    • New INS Board

      Pitch gyro stop working (actually it has not stopped working entirely but it is not reliable enough anymore). And ADXL335 has to be replaced by something more accurate and tolerant to vibrations. It means that original INS board (designed for quadrocopter) can be used for testing purposes only and will be replaced by something newer, faster, cuter and designed specifically for the heli.