📄 pwmgen.c
字号:
/********************************************************************* Description: pwmgen.c* A HAL component that generates Pulse Width and* Pulse Density Modulation** Author: John Kasunich* License: GPL Version 2* * Copyright (c) 2006 All rights reserved.** Last change: # $Revision: 1.14 $* $Author: jepler $* $Date: 2007/06/01 20:06:59 $********************************************************************//** This file, 'pwmgen.c', is a HAL component that generates Pulse Width Modulation or Pulse Density Modulation signals in software. Since the timing granularity of a software based scheme is rather large compared to a hardware, either the output frequency, or the resolution, or both, will be less than expected from hardware implementations. The driver exports two functions. 'pwmgen.make-pulses', is responsible for actually generating the PWM/PDM signals. It must be executed in a fast thread to reduce pulse jitter and improve resolution. The other function, pwmgen.update, is normally called from a much slower thread. It reads the command and sets internal variables used by 'pwm.make-pulses'. 'update' uses floating point, 'make-pulses' does not. Polarity: All signals from this module have fixed polarity (active high) If the driver needs the opposite polarity, the signals can be inverted using parameters exported by the hardware driver(s) such as ParPort.*//** Copyright (C) 2003 John Kasunich <jmkasunich AT users DOT sourceforge DOT net>*//** This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA THE AUTHORS OF THIS LIBRARY ACCEPT ABSOLUTELY NO LIABILITY FOR ANY HARM OR LOSS RESULTING FROM ITS USE. IT IS _EXTREMELY_ UNWISE TO RELY ON SOFTWARE ALONE FOR SAFETY. Any machinery capable of harming persons must have provisions for completely removing power from all motors, etc, before persons enter any danger area. All machinery must be designed to comply with local and national safety codes, and the authors of this software can not, and do not, take any responsibility for such compliance. This code was written as part of the EMC HAL project. For more information, go to www.linuxcnc.org.*/#ifndef RTAPI#error This is a realtime component only!#endif#include "rtapi.h" /* RTAPI realtime OS API */#include "rtapi_app.h" /* RTAPI realtime module decls */#include "hal.h" /* HAL public API decls */#define MAX_CHAN 8/* module information */MODULE_AUTHOR("John Kasunich");MODULE_DESCRIPTION("PWM/PDM Generator for EMC HAL");MODULE_LICENSE("GPL");#define MAX_OUTPUT_TYPE 2int output_type[MAX_CHAN] = { -1, -1, -1, -1, -1, -1, -1, -1 };RTAPI_MP_ARRAY_INT(output_type, 8, "output types for up to 8 channels");/************************************************************************ STRUCTURES AND GLOBAL VARIABLES *************************************************************************//* values for pwm_mode */#define PWM_DISABLED 0#define PWM_PURE 1#define PWM_DITHER 2#define PWM_PDM 3typedef struct { long period; /* length of PWM period, ns */ long high_time; /* desired high time, ns */ long period_timer; /* timer for PWM period */ long high_timer; /* timer for high time */ unsigned char curr_output; /* current state of output */ unsigned char output_type; unsigned char pwm_mode; unsigned char direction; hal_bit_t *out[2]; /* pins for output signals */ hal_bit_t *enable; /* pin for enable signal */ hal_float_t *value; /* command value */ hal_float_t scale; /* param: scaling from value to duty cycle */ hal_float_t offset; /* param: offset: this is added to duty cycle */ float old_scale; /* stored scale value */ double scale_recip; /* reciprocal value used for scaling */ hal_float_t pwm_freq; /* param: (max) output frequency in Hz */ float old_pwm_freq; /* used to detect changes */ int periods; /* number of periods in PWM cycle */ float periods_recip; /* reciprocal */ hal_bit_t dither_pwm; /* 0 = pure PWM, 1 = dithered PWM */ hal_float_t min_dc; /* param: minimum duty cycle */ hal_float_t max_dc; /* param: maximum duty cycle */ hal_float_t curr_dc; /* param: current duty cycle */} pwmgen_t;/* ptr to array of pwmgen_t structs in shared memory, 1 per channel */static pwmgen_t *pwmgen_array;#define PWM_PIN 0 /* output phase used for PWM signal */#define DIR_PIN 1 /* output phase used for DIR signal */#define UP_PIN 0 /* output phase used for UP signal */#define DOWN_PIN 1 /* output phase used for DOWN signal *//* other globals */static int comp_id; /* component ID */static int num_chan; /* number of pwm generators configured */static long periodns; /* makepulses function period in nanosec *//************************************************************************ LOCAL FUNCTION DECLARATIONS *************************************************************************/static int export_pwmgen(int num, pwmgen_t * addr, int output_type);static void make_pulses(void *arg, long period);static void update(void *arg, long period);/************************************************************************ INIT AND EXIT CODE *************************************************************************/int rtapi_app_main(void){ int n, retval; for (n = 0; n < MAX_CHAN && output_type[n] != -1 ; n++) { if ((output_type[n] > MAX_OUTPUT_TYPE) || (output_type[n] < 0)) { rtapi_print_msg(RTAPI_MSG_ERR, "PWMGEN: ERROR: bad output type '%i', channel %i\n", output_type[n], n); return -1; } else { num_chan++; } } if (num_chan == 0) { rtapi_print_msg(RTAPI_MSG_ERR, "PWMGEN: ERROR: no channels configured\n"); return -1; } /* periodns will be set to the proper value when 'make_pulses()' runs for the first time. We load a default value here to avoid glitches at startup */ periodns = 50000; /* have good config info, connect to the HAL */ comp_id = hal_init("pwmgen"); if (comp_id < 0) { rtapi_print_msg(RTAPI_MSG_ERR, "PWMGEN: ERROR: hal_init() failed\n"); return -1; } /* allocate shared memory for generator data */ pwmgen_array = hal_malloc(num_chan * sizeof(pwmgen_t)); if (pwmgen_array == 0) { rtapi_print_msg(RTAPI_MSG_ERR, "PWMGEN: ERROR: hal_malloc() failed\n"); hal_exit(comp_id); return -1; } /* export all the variables for each PWM generator */ for (n = 0; n < num_chan; n++) { /* export all vars */ retval = export_pwmgen(n, &(pwmgen_array[n]), output_type[n]); if (retval != 0) { rtapi_print_msg(RTAPI_MSG_ERR, "PWMGEN: ERROR: pwmgen %d var export failed\n", n); hal_exit(comp_id); return -1; } } /* export functions */ retval = hal_export_funct("pwmgen.make-pulses", make_pulses, pwmgen_array, 0, 0, comp_id); if (retval != 0) { rtapi_print_msg(RTAPI_MSG_ERR, "PWMGEN: ERROR: makepulses funct export failed\n"); hal_exit(comp_id); return -1; } retval = hal_export_funct("pwmgen.update", update, pwmgen_array, 1, 0, comp_id); if (retval != 0) { rtapi_print_msg(RTAPI_MSG_ERR, "PWMGEN: ERROR: update funct export failed\n"); hal_exit(comp_id); return -1; } rtapi_print_msg(RTAPI_MSG_INFO, "PWMGEN: installed %d PWM/PDM generators\n", num_chan); hal_ready(comp_id); return 0;}void rtapi_app_exit(void){ hal_exit(comp_id);}/************************************************************************ REALTIME STEP PULSE GENERATION FUNCTIONS *************************************************************************//** The pwm generator works by adding a positive value proportional to the desired duty cycle to accumulator. When the accumulator is greater than zero, the output goes high (if permitted by the maximum frequency). When the output is high, a larger value is subtracted from the accumulator. As a result, it oscillates around zero to generate PWM or PDM, based on the values that are added and subtracted.*/static void make_pulses(void *arg, long period){ pwmgen_t *pwmgen; int n; /* store period for use in update() function */ periodns = period; /* point to pwmgen data structures */ pwmgen = arg; for (n = 0; n < num_chan; n++) { switch ( pwmgen->pwm_mode ) { case PWM_PURE: if ( pwmgen->curr_output ) { /* current state is high, update cumlative high time */ pwmgen->high_timer += periodns; /* have we been high long enough? */ if ( pwmgen->high_timer >= pwmgen->high_time ) { /* yes, terminate the high time */ pwmgen->curr_output = 0; } } /* update period timer */ pwmgen->period_timer += periodns; /* have we reached the end of a period? */ if ( pwmgen->period_timer >= pwmgen->period ) { /* reset both timers to zero for jitter-free output */ pwmgen->period_timer = 0; pwmgen->high_timer = 0; /* start the next period */ if ( pwmgen->high_time > 0 ) { pwmgen->curr_output = 1; } } break; case PWM_DITHER: if ( pwmgen->curr_output ) { /* current state is high, update cumlative high time */ pwmgen->high_timer -= periodns; /* have we been high long enough? */ if ( pwmgen->high_timer <= 0 ) { /* yes, terminate the high time */ pwmgen->curr_output = 0; } } /* update period timer */ pwmgen->period_timer += periodns; /* have we reached the end of a period? */ if ( pwmgen->period_timer >= pwmgen->period ) { /* update both timers, retain remainder from last period */ /* this allows dithering for finer resolution */ pwmgen->period_timer -= pwmgen->period; pwmgen->high_timer += pwmgen->high_time; /* start the next period */ if ( pwmgen->high_timer > 0 ) { pwmgen->curr_output = 1; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -