stevieb has asked for the wisdom of the Perl Monks concerning the following question:

I'm having some issues getting one (or two) of my XS-wrapped C functions to work correctly, and I've been at this for a couple of days now. Essentially, the code generates and registers a custom character for an LCD display, and then displays it. I have added the XS code for the two functions, the function declarations, a C example that works (prints the defined char), and a Perl example that doesn't (print the char). I do not know whether it's the lcdPutchar() or lcdCharDef() that's the issue, as there's no way to read the register in the LCD for the registered char.

I apologize for the length. I'm hoping by posting this (per usual), that it'll just click. Otherwise, I'm hoping someone more familiar with C/XS can perhaps spot something.

In the lcd_char_def() Perl module function, I *think* I'm generating the proper data with pack() to send to the C function (as when I printf() out the values of the array, each elem matches what I send in with a C program directly.

Here is the XS code that covers the two external C functions:

void lcdCharDef(fd, index, data) int fd int index unsigned char * data void lcdPutchar(fd, data) int fd unsigned char data

Here is the C declarations:

extern void lcdCharDef(const int fd, int index, unsigned char data [8] +); extern void lcdPutchar(const int fd, unsigned char data);

Here is the Perl module code that calls the XS:

sub lcd_char_def { shift if @_ == 4; my ($fd, $index, $data) = @_; my $unsigned_char = pack "V0C*", @$data; lcdCharDef($fd, $index, $unsigned_char); } sub lcd_put_char { shift if @_ == 3; my ($fd, $data) = @_; lcdPutchar($fd, $data); }

A short C program which prints out the proper custom character to my LCD:

#include <stdio.h> #include <wiringPi.h> #include <lcd.h> int main (int argc, char *argv[]){ unsigned char newChar [8] = { 0b11111, 0b10001, 0b10001, 0b10101, 0b11111, 0b10001, 0b10001, 0b11111, }; wiringPiSetupGpio(); static int fd; fd = lcdInit(2, 16, 4, 23, 16, 5, 6, 13, 19, 0, 0, 0, 0); lcdClear(fd); lcdPosition(fd, 0, 0); lcdCharDef(fd, 2, newChar); lcdPutchar(fd, 2); sleep(1); return 0; }

...and the Perl code that *should* do the same thing (print the custom char):

use warnings; use strict; use WiringPi::API qw(:all); setup_gpio(); my %args = ( cols => 16, rows => 2, bits => 4, rs => 23, strb => 16, d0 => 5, d1 => 6, d2 => 13, d3 => 19, d4 => 0, d5 => 0, d6 => 0, d7 => 0, ); my $fd = lcd_init(%args); my $def = [ 0b11111, 0b10001, 0b10001, 0b10101, 0b11111, 0b10001, 0b10001, 0b11111, ]; lcd_clear($fd); lcd_position($fd, 0, 0); lcd_char_def($fd, 2, $def); lcd_put_char($fd, 2); sleep 1;

Is there anything glaring that I'm missing? All of my other Perl code works fine (ie. all wrapped C functions work properly in all other cases. It's just one or both of these two that don't).

  • Comment on [SOLVED (workaround)]: External C function called through XS not Doing The Right Thing
  • Select or Download Code

Replies are listed 'Best First'.
Re: External C function called through XS not Doing The Right Thing
by huck (Prior) on Mar 10, 2017 at 02:18 UTC

    My C is rusty, but

    If i saw

    char *strcpy (char *dest, char *src)
    I understand that strcopy takes pointers on the stack as its args. a web example shows
    char input_str[20]; strcpy(input_str, "Hello");
    so the prototype you gave of
    extern void lcdCharDef(const int fd, int index, unsigned char data [8] +);
    well unsigned char data [8] is not unsigned char *data. Does it mean the call stack has 8 characters in a row on it rather than a pointer to an array of 8 characters? if so your unsigned char * data in
    void lcdCharDef(fd, index, data) int fd int index unsigned char * data
    is putting a pointer to an array on the stack, isnt it?

    i dug up my old C books, one copyright 1990 even, and i cant quite figure out what unsigned char data [8] in a prototype means, everytime char strings are passed the prototype is unsigned char *data

    even looking on the web i dont see a prototype like unsigned char data [8], again all the calls seem to want unsigned char *data

    edit,I researched and wrote this as other replys were coming in. So your workaround seems to imply that the pointer is what should be on the stack anyway, does that mean a prototype of unsigned char data [8] is converted to a prototype of unsigned char *data because of the [] array reference? what does the 8 do then?

    edit2: ok, i should have hit the web harder, in most cases ive found a prototype of unsigned "something" data [8] uses some other type than char, like int. My first searches were for char arrays functions. In this case the 8 doesnt really mean anything and it is the same as unsigned char *data. If it were a 2d array or (N)d array they say all but the leftmost sizes need to be declared, even if you pass the array dims as parms. but void function_c(int m, int n, int arr[m][n]); is fine. this seems to be something introduced in C99, so i may need newer C books, 'cept i dont like to use it if i dont have to.

Re: External C function called through XS not Doing The Right Thing
by stevieb (Canon) on Mar 10, 2017 at 02:11 UTC

    Interesting. I found in the Perl script, if I print a string to the LCD prior to calling lcd_char_def() it works. So I took a step back and in the actual function itself, I added a call to lcdPuts(), and printing a newline (which is essentially a zero-width char on the display). This fixes it.

    sub lcd_char_def { shift if @_ == 4; my ($fd, $index, $data) = @_; lcdPuts($fd, "\n"); my $unsigned_char = pack "C[8]", @$data; lcdCharDef($fd, $index, $unsigned_char); }

    So I have a code problem somewhere it seems, but at least this is a cheesy workaround until I can track it down.

Re: External C function called through XS not Doing The Right Thing
by stevieb (Canon) on Mar 10, 2017 at 01:58 UTC

    I'm suspecting this isn't a Perl/XS problem, but I'm head scratching. After adding some print statements in core locations in the C code just prior to the data being sent to the LCD, I get the exact same output between the C example and the Perl example, which leads me to believe the way I'm sending data in through Perl/XS is fine:

    # C sndcmd: 40 sndcmd: 12 sndcmd: 12 sndcmd: 12 sndcmd: 1 sndcmd: 2 sndcmd: 6 sndcmd: 20 sndcmd: 1 sndcmd: 2 sndcmd: 128 sndcmd: 80 sndcmd: 31 sndcmd: 17 sndcmd: 17 sndcmd: 21 sndcmd: 31 sndcmd: 17 sndcmd: 17 sndcmd: 31 put: 2 sndcmd: 2 # Perl sndcmd: 40 sndcmd: 12 sndcmd: 12 sndcmd: 12 sndcmd: 1 sndcmd: 2 sndcmd: 6 sndcmd: 20 sndcmd: 1 sndcmd: 2 sndcmd: 128 sndcmd: 80 sndcmd: 31 sndcmd: 17 sndcmd: 17 sndcmd: 21 sndcmd: 31 sndcmd: 17 sndcmd: 17 sndcmd: 31 put: 2 sndcmd: 2

      Hi

      ? Are you sure about  "V0C*", ?

      You really want V An unsigned long (32-bit) in "VAX" (little-endian) order. Maybe you mean "C0" as in character mode?

        I am not 100% sure, I've tried a few things. I just know that with "V0C*", I get the exact same parameters from a C program as I do with the Perl script in the called function.

        I'll re-review the pack documentation now that I at least have a workaround.

Re: External C function called through XS not Doing The Right Thing (pack)
by Anonymous Monk on Mar 10, 2017 at 01:59 UTC

    Hi

    So it doesn't work how?

      My bad, I've updated the question.

      The custom character wasn't being printed to the LCD from a Perl script, but was from an equivalent C program.