Class: LGPIO::HardwarePWM

Inherits:
Object
  • Object
show all
Defined in:
lib/lgpio/hardware_pwm.rb,
ext/lgpio/lgpio.c

Direct Known Subclasses

Infrared, PositionalServo

Constant Summary collapse

NS_PER_S =
1_000_000_000
NS_PER_US =
1_000
SYS_FS_PWM_PATH =
"/sys/class/pwm/"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(chip, channel, frequency: nil, period: nil) ⇒ HardwarePWM

Returns a new instance of HardwarePWM.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/lgpio/hardware_pwm.rb', line 9

def initialize(chip, channel, frequency: nil, period: nil)
  @chip    = chip
  @channel = channel

  # Accept either frequency (in Hz) or period in nanoseconds.
  if (frequency && period) || (!frequency && !period)
    raise "either period: or frequency: is required, but not both"
  end

  period ? self.period = period : self.frequency = frequency

  # Default to with 0 duty cycle and normal polarity.
  self.duty = 0
  self.polarity = :normal

  enable
end

Instance Attribute Details

#dutyObject

Returns the value of attribute duty.



7
8
9
# File 'lib/lgpio/hardware_pwm.rb', line 7

def duty
  @duty
end

#enabledObject (readonly)

Returns the value of attribute enabled.



7
8
9
# File 'lib/lgpio/hardware_pwm.rb', line 7

def enabled
  @enabled
end

#periodObject

Returns the value of attribute period.



7
8
9
# File 'lib/lgpio/hardware_pwm.rb', line 7

def period
  @period
end

#polarityObject

Returns the value of attribute polarity.



7
8
9
# File 'lib/lgpio/hardware_pwm.rb', line 7

def polarity
  @polarity
end

Instance Method Details

#disableObject



99
100
101
102
# File 'lib/lgpio/hardware_pwm.rb', line 99

def disable
  File.open(enable_path, 'w') { |f| f.write("0") }
  @enabled = false
end

#duty_pathObject



39
40
41
# File 'lib/lgpio/hardware_pwm.rb', line 39

def duty_path
  @duty_path ||= "#{path}duty_cycle"
end

#duty_percentObject



77
78
79
80
# File 'lib/lgpio/hardware_pwm.rb', line 77

def duty_percent
  return 0.0 if (!duty || !period) || (duty == 0)
  (duty / period.to_f) * 100.0
end

#duty_percent=(d) ⇒ Object



82
83
84
85
86
# File 'lib/lgpio/hardware_pwm.rb', line 82

def duty_percent=(d)
  raise "duty_cycle: #{d} % cannot be more than 100%" if d > 100
  d_ns = ((d / 100.0) * @period.to_i).round
  self.duty = d_ns
end

#duty_us=(d_us) ⇒ Object



88
89
90
91
# File 'lib/lgpio/hardware_pwm.rb', line 88

def duty_us=(d_us)
  d_ns = (d_us * NS_PER_US).round
  self.duty = d_ns
end

#enableObject



104
105
106
107
# File 'lib/lgpio/hardware_pwm.rb', line 104

def enable
  File.open(enable_path, 'w') { |f| f.write("1") }
  @enabled = true
end

#enable_pathObject



43
44
45
# File 'lib/lgpio/hardware_pwm.rb', line 43

def enable_path
  @enable_path ||= "#{path}enable"
end

#frequencyObject



62
63
64
65
# File 'lib/lgpio/hardware_pwm.rb', line 62

def frequency
  # If not set explicitly, calculate from period, rounded to nearest Hz.
  @frequency ||= (1_000_000_000.0 / period).round
end

#frequency=(freq) ⇒ Object



57
58
59
60
# File 'lib/lgpio/hardware_pwm.rb', line 57

def frequency=(freq)
  self.period = (NS_PER_S / freq.to_f).round
  @frequency = freq
end

#pathObject



27
28
29
# File 'lib/lgpio/hardware_pwm.rb', line 27

def path
  @path ||= "#{SYS_FS_PWM_PATH}pwmchip#{@chip}/pwm#{@channel}/"
end

#period_pathObject



35
36
37
# File 'lib/lgpio/hardware_pwm.rb', line 35

def period_path
  @period_path ||= "#{path}period"
end

#polarity_pathObject



31
32
33
# File 'lib/lgpio/hardware_pwm.rb', line 31

def polarity_path
  @polarity_path ||= "#{path}polarity"
end

#tx_wave_ook(dutyPath, dutyString, pulses) ⇒ Object

**************************************************************************



231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'ext/lgpio/lgpio.c', line 231

static VALUE tx_wave_ook(VALUE self, VALUE dutyPath, VALUE dutyString, VALUE pulses) {
  // NOTE: This uses hardware PWM, NOT the lgpio software PWM/wave interface.
  // The Ruby class LGPIO::HardwarePWM should have already set the PWM carrier frequency.
  //
  // Convert pulses from microseconds to nanoseconds.
  Check_Type(pulses, T_ARRAY);
  uint32_t pulseCount = rb_array_len(pulses);
  uint64_t nanoPulses[pulseCount];
  for (uint32_t i=0; i<pulseCount; i++) {
    nanoPulses[i] = NUM2UINT(rb_ary_entry(pulses, i)) * 1000;
  }

  // Prepare to write duty cycle.
  const char *filePath = StringValueCStr(dutyPath);
  FILE *dutyFile = fopen(filePath, "w");
  if (dutyFile == NULL) {
    VALUE errorMessage = rb_sprintf("Could not open PWM duty_cycle file: %s", filePath);
    rb_raise(rb_eRuntimeError, "%s", StringValueCStr(errorMessage));
  }
  fclose(dutyFile);
  const char *cDuty = StringValueCStr(dutyString);

  // Toggle duty cycle between given value and 0, to modulate the PWM carrier.
  for (uint32_t i=0; i<pulseCount; i++) {
    if (i % 2 == 0) {
      dutyFile = fopen(filePath, "w");
      fputs(cDuty, dutyFile);
      fclose(dutyFile);
    } else {
      dutyFile = fopen(filePath, "w");
      fputs("0", dutyFile);
      fclose(dutyFile);
    }
    // Wait for pulse time.
    nanoDelay(nanoPulses[i]);
  }
  // Leave the pin low.
  dutyFile = fopen(filePath, "w");
  fputs("0", dutyFile);
  fclose(dutyFile);
  return UINT2NUM(pulseCount);
}