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 =
10**9
NS_PER_US =
10**3
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



97
98
99
100
# File 'lib/lgpio/hardware_pwm.rb', line 97

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



75
76
77
78
# File 'lib/lgpio/hardware_pwm.rb', line 75

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

#duty_percent=(d) ⇒ Object



80
81
82
83
84
# File 'lib/lgpio/hardware_pwm.rb', line 80

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



86
87
88
89
# File 'lib/lgpio/hardware_pwm.rb', line 86

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

#enableObject



102
103
104
105
# File 'lib/lgpio/hardware_pwm.rb', line 102

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 ||= (NS_PER_S / period.to_f).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);
}