Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Last updated: Wednesday 29 October 2025 @ 14:52:53

ADC to PWM functionality

Warning

Updating the PWM library

We need to add the a new prototype to the bbb_pwm.h and the body to the bbb_pwm.c files, that converts the ADC value to a the correct PWM duty cycle against the specified period.

Note

  • Remember that the Beaglebone blacks ADC chip does not use 8bit levels, i.e 0-255 like you would find on a Atmel328p.

  • It uses provides the duty cycle as % of the period.

  1. Open the bbb_pwm.h file and modify to include the prototype below called map:

    Suppressed code here [8 lines]

    #ifndef BBB_PWM_H
    #define BBB_PWM_H
    
    ...
    
    unsigned int map(u_int16_t adc_value, unsigned int period_ns);
    
    #endif
    
  2. To map we need to get the relationship between adc and the period to get the duty cycle

    \[ \text{duty_cycle_ns} = \frac{\text{adc_value} \cdot \text{period_ns}}{4095} \]

  3. We now can implement above equation by modifying the bbb_pwm.c file to include the body of the previously defined prototype, place the function at the bottom of the file:

    Suppressed code here [15 lines]

    ... 
    
    // Map ADC value to PWM duty cycle with user-specified period
    unsigned int map(u_int16_t adc_value, unsigned int period_ns) {
    
        if (adc_value > 4095) {
            fprintf(stderr, "ADC value out of range\n");
            return 1;
        }
    
        // Map ADC value (0 to 4095) to PWM duty cycle (0 to period_ns)
        unsigned int duty_cycle_ns = (adc_value * period_ns) / 4095;
    
        return duty_cycle_ns;
    }
    
  4. As the Makefile already exists and does not need updating, simply remake the library:

    Terminal

    make clean
    make uninstall
    make install
    

Implementing an example

We are going to create a script that utlises what we did for ~/examples_C/adc_read.c and examples_C/pwm_test.c, by utilising both libraries bbb_pwm.h and bbb_adc.h.

  1. Create a new directory in the home directory and call it ~/examples_C/adc2pwm and inside a file called adc2pwm.c

    Terminal

    mkdir ~/examples_C/adc2pwm && cd ~/examples_C/adc2pwm && touch adc2pwm.c
    
  2. Modify the contents of adc2pwm.c with the following code:

    Suppressed code here [61 lines]

    #include <bbb_pwm.h>
    #include <bbb_adc.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdint.h>
    
    int main() {
        PWM pwm;
        unsigned int adc_reading = 0;
        unsigned int user_defined_period_ns = 1000000;
        unsigned int duty_cycle = 0;
    
        // Initialize PWM on a specified pin, for example "P9_14"
        pwm_init(&pwm, "P9_14");
    
        // Set initial PWM period
        pwm_set_period(&pwm, user_defined_period_ns);
    
        // Enable PWM output
        pwm_enable(&pwm);
    
        printf("Phy: %s\nChannel: %s\nChip: %s\nPeriod path: %s\nDuty Cycle path: %s\nEnable path: %s\n",
                                                                                    pwm.phy_pin,
                                                                                    pwm.channel,
                                                                                    pwm.chip,
                                                                                    pwm.period_path,
                                                                                    pwm.duty_cycle_path,
                                                                                    pwm.enable_path);
    
        // Loop to read ADC values and update PWM duty cycle
        for (int i = 0; i < 40; i++) {
            adc_get_value(0, &adc_reading); // Read ADC value from channel 0
            printf("ADC Reading: %u\n", adc_reading);
    
            // Map the ADC reading to the PWM duty cycle and update it
            duty_cycle = map(adc_reading, user_defined_period_ns);
    
            pwm_set_duty_cycle(&pwm, duty_cycle);
    
            sleep(1); // Update every 1 second
        }
    
        // Disable PWM and clean up
        pwm_disable(&pwm);
        pwm_cleanup(&pwm);
    
        return 0;
    }
    
  3. Remember to create a Makefile file:

    Suppressed code here [25 lines]

    # Compiler and flags
    CC = gcc
    CFLAGS = -Wall -Werror
    
    # Target executable name
    TARGET = adc2pwm
    
    # Source files
    SRC = adc2pwm.c
    
    # Libraries to link against
    LIBS = -lbbb_pwm -lbbb_adc
    
    # Default target: build the executable
    all: $(TARGET)
    
    # Build the executable
    $(TARGET): $(SRC)
            $(CC) $(CFLAGS) $(SRC) $(LIBS) -o $(TARGET)
    
    # Clean up build artifacts
    clean:
            rm -f $(TARGET)
    
    # Phony targets to avoid conflicts with files of the same name
    .PHONY: all clean
    
  4. Setup the following circuit, which is an amalgamation of ADC C Library and PWM C Library circuits


  5. When running ./adc2pwm you may see this terminal output and you have connected up your circuity correctly, the LED should change it's brightness based on the resistance from the potentiometer:

    Output

    Phy: P9_14
    Channel: 0
    Chip: 0
    Period path: /sys/class/pwm/pwmchip0/pwm0/period
    Duty Cycle path: /sys/class/pwm/pwmchip0/pwm0/duty_cycle
    Enable path: /sys/class/pwm/pwmchip0/pwm0/enable
    ADC Reading: 0
    ADC Reading: 0
    ADC Reading: 283
    ADC Reading: 697
    ADC Reading: 1313
    ADC Reading: 1674
    ADC Reading: 1907
    ADC Reading: 2454
    ADC Reading: 2968
    ADC Reading: 3367
    ADC Reading: 3687
    ADC Reading: 3989
    ADC Reading: 3920
    ADC Reading: 4001
    ADC Reading: 3932
    ADC Reading: 3985
    ADC Reading: 4002
    ADC Reading: 4002
    ADC Reading: 4001
    ...
    
  6. Once you have done that, experiment with the code, can you get a faster response, or a longer run time of the program?