2
votes

I wrote a sample pwm function in C for beaglebone black. Whenever I make a function call in other modules or in main(), I end up in segmentation fault. Kindly help where I am making the mistake and how to deal with this issue. Below is the code.

int trigger_pwm_output(unsigned input_no )
{
    FILE *pwm,*duty,*period,*run;``
    uint16_t input=0;
    uint8_t input_no=0,input_state=0;
    unsigned int duty_cycle =500000;
    pwm = fopen("/sys/devices/bone_capemgr.9/slots", "w");
    fseek(pwm,0,SEEK_SET);
    fprintf(pwm,"am33xx_pwm");
    fflush(pwm);



    switch(input_no)
    {
        case 0:

            fprintf(pwm,"bone_pwm_P8_13");
            fflush(pwm);
            period = fopen("/sys/devices/ocp.3/pwm_test_P8_13.15/period", "w");
            fseek(period,0,SEEK_SET);
            fprintf(period,"%d",500000);
            fflush(period);

            duty = fopen("/sys/devices/ocp.3/pwm_test_P8_13.15/duty", "w");
            fseek(duty,0,SEEK_SET);
            run = fopen("/sys/devices/ocp.3/pwm_test_P8_13.15/run", "w");
            fseek(run,0,SEEK_SET);
            fprintf(run,"%d",0);
            fflush(run);

            fseek(run,0,SEEK_SET);
            count++;
                do 
                {
                    duty_cycle += 10;
                    fprintf(duty,"%d",duty_cycle);
                }while(count > 0) || (count < 10));
                fflush(duty);
                fprintf(run,"%d",1);
                fflush(run);

            fclose(pwm);
            fclose(duty);
            fclose(period);
            fclose(run);
            break;
        case 1:
            fprintf(pwm,"bone_pwm_P8_19");
            fflush(pwm);
            period = fopen("/sys/devices/ocp.3/pwm_test_P8_19.16/period", "w");
            fseek(period,0,SEEK_SET);
            fprintf(period,"%d",500000);
            fflush(period);

            duty = fopen("/sys/devices/ocp.3/pwm_test_P8_19.16/duty", "w");
            fseek(duty,0,SEEK_SET);
            run = fopen("/sys/devices/ocp.3/pwm_test_P8_19.16/run", "w");
            fseek(run,0,SEEK_SET);
            fprintf(run,"%d",0);
            fflush(run);

            fseek(run,0,SEEK_SET);
            --count;
            do 
            {
                duty_cycle += 10;
                fprintf(duty,"%d",duty_cycle);
            }while(count <10);
            fflush(duty);
            fprintf(run,"%d",1);
            fflush(run);



            fclose(pwm);
            fclose(duty);
            fclose(period);
            fclose(run);
            break;



    }
    return 0;
}

`

2
How does that even compile? You have a name collision between a function parameter input_no and an ordinary local variable of the same name (and different type).John Bollinger
Once you fix the name collision, you should add code to check the results of substantially all your function calls. You assume that they all will succeed. My first guess at why valid code similar to what you posted might segfault would be that one or more of your fopen() calls fails, in which case it will return a null pointer. When you subsequently try to perform I/O via such a pointer, a segfault is a likely (but by no means guaranteed) result.John Bollinger
As wrote @JohnBollinger, check that the opening of each file is successful by typing FILE* fp=fopen("bla.txt","w");if(fp==NULL){printf("failed to openfile\n");exit(1);} . It is likely the case, since you try to open files in the directory /sys and it likely requires root privileges.francis
I smell an endless loop. wont this conditional always be true: while(count > 0) || (count < 10))Octopus

2 Answers

6
votes

I have just successfully enabled 4 PWM outputs on BeagleBone Black using C, so I can hopefully give you some legit advices.

Using files under /sys/devices/ocp.3/ directory (known as the Device Tree Overlay) for PWM is not a good idea for two reasons:

  • The last two digits of PWM pin directory name are arbitrary, e.g. pwm_test_P8_13.xx, a potential cause for segmentation fault since you don't check for fopen status. However, fopen does not allow wildcard in the specified path. My workaround, which was not clean at all, was to call echo /sys/devices/ocp.3/pwm_test_P8_13.*/ with popen to retrieve the correct full path for the PWM pin directory.
  • There are only two PWM modules on BeagleBone Black, and the DTO prevents you from using more than one PWM pin per module. Consequently, if you try to modify the period of a pin from command line, a write error may return. If you then type dmesg | tail, you may see the following message:

    [ 1406.652632] ehrpwm 48304200.ehrpwm: Period value conflicts with channel 1
    [ 1406.660047] pwm_test pwm_test_P8_19.11: pwm_config() failed

As such, I used the files under /sys/class/pwm/ instead. You still need to write am33xx_pwm and the PWM pins you plan to use to the file /sys/devices/bone_capemgr.9/slots. Then, you go to directory /sys/class/pwm/, write digit (0-7) to the file export, then modify the file under generated directory pwm?, where ? is the digit written to export. The following table shows each digit and their corresponding pin(s):

enter image description here

Here is the tutorial I referred to.

Hope this helps!

0
votes

There are several issues with this code - some of which are already described in the comments.

  • There is a variable name collision. input_no is both passed into the function as well as defined within the function.
  • Both do-while loops are potentially infinite. Both loops have an end condition dependent on count which is not modified in the body of either loop. Additionally, the condition in the first of these loops is always true regardless of the value of count.
  • The return value of all fopen calls is never checked. If any of these calls fail, then subsequent file operations are called with null file pointers.
  • count is not defined or initialized anywhere in this code. Is it defined globally?
  • One of your calls to fopen is made outside the body of the switch statement - it'd be best to move its corresponding close call outside as well.