zoukankan      html  css  js  c++  java
  • 新西达电调初始化代码,使用nodejs ffi技术调用wiringpi,代码使用typescript编写

    这是我设计的F450四轴飞行器飞控代码的一部分

    运行在orangepi-zero上,操作系统是armbian,思路是使用node-ffi调用wiringpi的so库与GPIO通信,然后控制端逻辑代码使用typescript编写

    需要注意的是node-ffi目前不支持node11版本以及以上,我使用的node版本是10

    ffi对so库发起调用的效率并不高,但是在这里依然是可以满足通信的需求了

    这里使用typescript封装了一个电机类,注释比较多,可以方便参考

    下面是电机类

      1 import { GPIO } from '../gpio';
      2 import WPIFFI from '../wiringpi-ffi';
      3 
      4 /**
      5  * 电机类
      6  * 封装了PWM,电调的初始化方法以及电机档位的控制方法
      7  */
      8 export class Motor {
      9   /**
     10    * 控制电机的GPIO口
     11    */
     12   private gpio: GPIO;
     13   /**
     14    * 是否已经初始化PWM
     15    */
     16   private pwmInitialized: boolean;
     17   /**
     18    * 是否已经初始化电调
     19    */
     20   private controllerInitialized: boolean;
     21   /**
     22    * 当前电机转速档位
     23    */
     24   private gear: number;
     25 
     26   /**
     27    * 获取控制电机的GPIO口
     28    */
     29   public get GPIO(): GPIO {
     30     return this.gpio;
     31   }
     32   /**
     33    * 获取PWM的初始化状态
     34    */
     35   public get PWMInitialized() {
     36     return this.pwmInitialized;
     37   }
     38   /**
     39    * 获取电调的初始化状态
     40    */
     41   public get ControllerInitialized() {
     42     return this.controllerInitialized;
     43   }
     44   /**
     45    * 获取电机的初始化状态(PWM且电调)
     46    */
     47   public get Initialized() {
     48     return this.PWMInitialized && this.ControllerInitialized;
     49   }
     50   /**
     51    * 获取电机的当前档位
     52    */
     53   public get Gear(): number {
     54     return this.gear;
     55   }
     56 
     57   /**
     58    * 调用FFI初始化PWM
     59    * @param value 初始化的脉冲宽度值
     60    * @param range 脉冲可调范围
     61    */
     62   private pwmInit(value: number = 0, range: number = 200) {
     63     console.log(`[${this.gpio}] init: ${value} ${range}`);
     64     if (WPIFFI.softPwmCreate(this.gpio, value, range)) {
     65       throw(new Error(`[${this.gpio}] pwm initialization failed`));
     66     }
     67   }
     68   /**
     69    * 调用FFI设置PWM脉冲
     70    * @param value 脉冲值,默认配置下,此值的范围为[0 ~ 200]
     71    */
     72   private pulseSet(value: number) {
     73     if (!this.PWMInitialized) {
     74       console.log(`[${this.gpio}] pwm not initialized`);
     75       return;
     76     }
     77     // 设置脉冲
     78     console.log(`[${this.gpio}] set: ${value}`);
     79     WPIFFI.softPwmWrite(this.gpio, value);
     80   }
     81 
     82   /**
     83    * 初始化PWM,即使能GPIO口的PWM时钟
     84    * 这里设置时钟为200个单位,即:1s / (200 * 100us) = 50hz
     85    */
     86   public PWMInit(): void {
     87     if (!this.PWMInitialized) {
     88       this.pwmInitialized = true;
     89       this.pwmInit(0, 200);
     90     } else {
     91       console.log(`[${this.gpio}] pwm already initialized`);
     92     }
     93   }
     94   /**
     95    * @async
     96    * 初始化控制电机的电调
     97    * 因为电调初始化协议有时序性,所以这里使用setTimeout异步延时发送初始化信号
     98    * 整个初始化过程会异步等待约10秒钟
     99    * 如不能确定电机状态,请谨慎调用,二次初始化会导致油门开到最大
    100    * 新西达电调的初始化协议,这里简单描述一下
    101    * 1.初始化PWM时钟,使其能在GPIO口产生PWM信号(本程序的PWM频率为:1s / (200 * 100us) = 50hz,11个档位)
    102    * 2.输出2ms的PWM脉冲,为设定的油门最大值
    103    * 3.听到短促的滴滴声音后,输出1ms的PWM脉冲,设定的油门最小值
    104    * 4.等待几秒钟之后,发送1ms~2ms之间的PWM脉冲,即可启动电机
    105    */
    106   public ControllerInit(): Promise<void> {
    107     return new Promise<void>((resolve, reject) => {
    108       // 如果PWM没有初始化则报错
    109       if (!this.PWMInitialized) {
    110         reject(`[${this.gpio}] pwm not initialized`);
    111       }
    112       // 如果电调并未初始化
    113       if (!this.ControllerInitialized) {
    114         // 这里先设置了标志,防止异步重入的错误
    115         this.controllerInitialized = true;
    116         // 发送高脉冲
    117         this.pulseSet(20);
    118         // 延时发送低脉冲
    119         setTimeout(() => {
    120           this.pulseSet(10);
    121           // 等待初始化完成返回
    122           setTimeout(() => {
    123             resolve();
    124           }, 7000);
    125         }, 3000);
    126       } else {
    127         console.log(`[${this.gpio}] controller already initialized`);
    128         resolve();
    129       }
    130     });
    131   }
    132   /**
    133    * @async
    134    * 初始化电机
    135    * 首先会初始化控制电机的GPIO口以使能PWM信号
    136    * 其次会初始化控制电机的电调并异步等待完成
    137    */
    138   public async Init(): Promise<void> {
    139     this.PWMInit();
    140     await this.ControllerInit();
    141   }
    142   /**
    143    * 设置电机档位
    144    * @param gear 电机档位,可调范围为[0 ~ 10]
    145    */
    146   public GearSet(gear: number): void {
    147     if (!this.PWMInitialized) {
    148       console.log(`[${this.gpio}] pwm not initialized`);
    149       return;
    150     }
    151     if (!this.ControllerInitialized) {
    152       console.log(`[${this.gpio}] controller not initialized`);
    153       return;
    154     }
    155     if (gear < 0 || gear > 10) {
    156       console.log(`[${this.gpio}] the range of gear must be [0 ~ 10]`);
    157       return;
    158     }
    159     const floorGear = Math.floor(gear);
    160     // 实际脉冲范围为[10 ~ 20]
    161     const value = floorGear + 10;
    162     // 设置脉冲信号
    163     this.pulseSet(value);
    164     // 写入当前档位
    165     this.gear = floorGear;
    166   }
    167   /**
    168    * 设置电机档位并持续一段时间后退回之前的档位
    169    * @param gear 电机档位,可调范围为[0 ~ 10]
    170    * @param s 档位保持的时间,单位秒,超出此时间之后档位将会退回到之前的状态
    171    * @param keep 是否回退
    172    */
    173   public GearSetTimeout(
    174     gear: number,
    175     s: number,
    176     keep: boolean = false,
    177   ): Promise<void> {
    178     return new Promise<void>((resolve) => {
    179       const ms = Math.floor(s * 1000);
    180       const bakGear = this.gear;
    181       this.GearSet(gear);
    182       setTimeout(() => {
    183         if (!keep) {
    184           this.GearSet(bakGear);
    185         }
    186         resolve();
    187       }, ms);
    188     });
    189   }
    190   /**
    191    * 在控制台输出设备的详情信息
    192    */
    193   public Detail(): void {
    194     console.log(`GPIO: ${this.GPIO}`);
    195     console.log(`PWMInitialized[true/false]: ${this.PWMInitialized}`);
    196     console.log(`ControllerInitialized[true/false]: ${this.ControllerInitialized}`);
    197     console.log(`Gear[0 ~ 10]: ${this.Gear}`);
    198   }
    199   /**
    200    * @constructor 构造函数,创建一个可用的电机对象
    201    * @param gpio 控制电机的GPIO口,具体请查看实际硬件连接与OrangePi Zero的GPIO定义
    202    */
    203   public constructor(gpio: GPIO) {
    204     this.gpio = gpio;
    205     this.pwmInitialized = false;
    206     this.controllerInitialized = false;
    207     this.gear = 0;
    208   }
    209 }
  • 相关阅读:
    FFmpeg 协议初步学习
    HTML DOM(一):认识DOM
    ant 安装
    ubunut 查看port被哪个程序占用
    cacti气象图调整(批量位置调整、更改生成图大小等)
    内网port映射具体解释(花生壳)
    HDU 2647 Reward(图论-拓扑排序)
    白话经典算法系列之七 堆与堆排序
    Codeforces Round #191 (Div. 2)---A. Flipping Game
    Serverlet具体解释
  • 原文地址:https://www.cnblogs.com/jimaojin/p/11855939.html
Copyright © 2011-2022 走看看