Error calling C module

Hello All,
In my ongoing saga trying to use PWM single shot to create my PPM output signal, I have found that, no matter what I try I cannot get the low part of the pulse to be shorter than about 750us. I need it to be within a few percent of 300us, but I just can’t get it that short. I suspect the reason is that there is a lot of overhead with the pwm.write command in single shot mode (i.e. with npulses=1) as it’s doing a write for every pulse, and the processor just can’t get the pulse short enough. (i’m using an Arudino Due, 84MHz)
So I thought I’d have a go at doing it with a C module using vHalPwmStart. I thought that maybe this would not have quite as much overhead as using pwm.write, and anyway it would be an interesting exercise to explore the C/Python interface.
I set everything up and tried to compile, but got this error.


[info] Searching for /home/ian/zerynth/C_Language_Interface_orig/__builtins__.py
[info] Searching for /home/ian/.zerynth2/dist/r2.5.0/stdlib/__builtins__.py
[info] ########## STEP 0 - first pass
[info] Compiling module: __main__ @ /home/ian/zerynth/C_Language_Interface_orig/main.py
[info] Searching for /home/ian/zerynth/C_Language_Interface_orig/__builtins__.py
[info] Searching for /home/ian/.zerynth2/dist/r2.5.0/stdlib/__builtins__.py
[info] Compiling module: __builtins__ @ /home/ian/.zerynth2/dist/r2.5.0/stdlib/__builtins__.py
[info] Searching for /home/ian/zerynth/C_Language_Interface_orig/streams.py
[info] Searching for /home/ian/.zerynth2/dist/r2.5.0/stdlib/streams.py
[info] Compiling module: streams @ /home/ian/.zerynth2/dist/r2.5.0/stdlib/streams.py
[fatal] Unexpected exception
Traceback (most recent call last):
  File "/home/ian/.zerynth2/dist/r2.5.0/ztc/compiler/compilercmd.py", line 80, in _zcompile
    binary, reprs = compiler.compile()
  File "/home/ian/.zerynth2/dist/r2.5.0/ztc/compiler/compiler.py", line 505, in compile
    self.compileModule(self.mainfile)
  File "/home/ian/.zerynth2/dist/r2.5.0/ztc/compiler/compiler.py", line 470, in compileModule
    mc.visit(tree)
  File "/home/ian/.zerynth2/sys/python/lib/python3.5/ast.py", line 245, in visit
    return visitor(node)
  File "/home/ian/.zerynth2/dist/r2.5.0/ztc/compiler/astwalker.py", line 103, in visit_Module
    self.code.addCode(self.visit(stmt))
  File "/home/ian/.zerynth2/sys/python/lib/python3.5/ast.py", line 245, in visit
    return visitor(node)
  File "/home/ian/.zerynth2/dist/r2.5.0/ztc/compiler/astwalker.py", line 786, in visit_FunctionDef
    ncfprms = self.isNativeC(node)
  File "/home/ian/.zerynth2/dist/r2.5.0/ztc/compiler/astwalker.py", line 689, in isNativeC
    vbl.append(s.s)
AttributeError: 'Name' object has no attribute 's'

Not sure what I’ve done wrong. Here is the Python module.

#-------------------------------------------------------------------------------
# C Interface
#
#
#-------------------------------------------------------------------------------

import streams
streams.serial()

myChan= [1000, 1500, 2000, 1500, 1000, 1500, 2000, 1500, 8100]

@c_native("initPWM",["PWMdriver.c"],[VHAL_PWM])
def PWMinit():
    pass

PWMinit()

@c_native("generatePulse", ["PWMdriver.c"], [VHAL_PWM])
def genPulse(pin, period, pulse, npulses):
    pass

while True:
    try:
        retVal = genPulse(D9, 2000, 1000, 1) # pin, period, pulse width, npulses
        # if retVal != PNONE:
        #     print("call failed", retVal, type(retVal)) # type is "PDRIVER", retVal is "NONE"
    except Exception as e:
        print(e)
# pwm write with npulses > 0 should block

and here is the C module.

//this is mandatory
#include "zerynth.h"

C_NATIVE(initPWM){
    C_NATIVE_UNWARN();
    vhalInitPWM();
    return ERR_OK; //<-- execution ok
}

C_NATIVE(generatePulse){
    C_NATIVE_UNWARN();
    int32_t vpin, period, pulse, npulses;
    if (parse_py_args("siii",nargs,args,&vpin, &period, &pulse, &npulses) != 4)
    	return ERR_TYPE_EXC;   //<-- this is the macro
    vpinId = PIN_CLASS_ID(vpin)
    prdTime = TIME_U(period, MICROS)
    plsTime = TIME_U(pulse, MICROS)
    vhalPwmStart(vpinId, prdTime, plsTime, npulses);
    return ERR_OK; //<-- execution ok
}

There are a few guesses in this module. The documentation notes that the time values must conform to TIME_U format. I found a TIME_U macro that took a number and a time unit, so I thought that might be the preferred way to specify a time. The same for the pin id, which must be an integer according to the documentation. I guessed that PIN_CLASS_ID might convert a pin name (like D9) to the correct pin number, so that’s why that’s there.
But my main problem is the error.
All advice gratefully received.

Hi @mogplus8
you missed a few semicolons in your C code, the pin could be parsed as integer and the quoted “VHAL_PWM”.

I think this compiles with no errors:

import streams
streams.serial()

myChan= [1000, 1500, 2000, 1500, 1000, 1500, 2000, 1500, 8100]

@c_native("initPWM",["PWMdriver.c"],["VHAL_PWM"])
def PWMinit():
    pass

PWMinit()

@c_native("generatePulse", ["PWMdriver.c"], ["VHAL_PWM"])
def genPulse(pin, period, pulse, npulses):
    pass

while True:
    try:
        retVal = genPulse(D9, 2000, 1000, 1) # pin, period, pulse width, npulses
        # if retVal != PNONE:
        #     print("call failed", retVal, type(retVal)) # type is "PDRIVER", retVal is "NONE"
    except Exception as e:
        print(e)
# pwm write with npulses > 0 should block

Your C code:

//this is mandatory
#include "zerynth.h"

C_NATIVE(initPWM){
    C_NATIVE_UNWARN();
    vhalInitPWM();
    return ERR_OK; //<-- execution ok
}

C_NATIVE(generatePulse){
    C_NATIVE_UNWARN();
    int32_t vpin, period, pulse, npulses;
    if (parse_py_args("iiii",nargs,args,&vpin, &period, &pulse, &npulses) != 4)
    	return ERR_TYPE_EXC;   //<-- this is the macro
    //vpinId = PIN_CLASS_ID(vpin)
    //prdTime = TIME_U(period, MICROS);
    //plsTime = TIME_U(pulse, MICROS);
    vhalPwmStart(vpin, TIME_U(period, MICROS), TIME_U(pulse, MICROS), npulses);
    return ERR_OK; //<-- execution ok
}

Let me know if this works :slight_smile:

:holding head in hands: Thanks Karim. Now I feel like a complete noob. What a maroon.

:-\

@karimhamdy1, I finally had a modicum of success with this particular part of the project. I finally got everything sorted out (with your help, thank you for that) and it is now running. I’m happy to say I can now get a down pulse of around 320us most of the time. I wanted 300us but 320 is close enough for rock’n’roll. My understanding of RC receivers is that they are not that critical about pulse timing. I guess I’ll find out when I plug one in.
I had to do a massive, awful, dirty kludge to get it to work though. I find that, as already discussed, it seems the PWM start command with npulses > 0, despite what the documentation says, does not block. What this means is that the next PWM start command is executed before the last pulse has finished, which meant that I only got one or two pulses in between the sync pulses (of 8100 us), not the whole eight. To get around this I had to put a “sleep” command (using the vosTicks trick you showed me) to ensure the next PWM start wasn’t executed until the last one completed. Awful, but it kinda works.
The only fly in the ointment is that the first down pulse (after the 8100us “sync” pulse), instead of being 300us (+/- 5%) is roughly 550us. This may still be okay but I’ll have to try it with a physical receiver to verify.
I was looking at vosLock and vosEvent to maybe lock the thread while the PWM pulse was being output, but I couldn’t get them to work as I don’t understand the syntax. Can you point me to any examples of how to use these things? Not sure they’ll help though, but happy to do some experimenting.

In the meantime I’ve got a few other ideas I want to try. I’ll keep you posted.

:wink:

Hi @mogplus8
no sweat, You’re welcome :slight_smile:
If you want an example for vosEvents, have a look at
zerynth2\dist\r2.5.0\libs\official\murata\lbee5kl1dx\csrc\lbee_ifc.c
zerynth2\dist\r2.5.0\libs\official\infineon\xmc4eth\csrc\eth_ifc.c

you can search for vosSysLock in:
zerynth2\dist\r2.5.0\vhal\armcmx\atmelsamc21\vhal_i2c.c

when you finish the project, try publishing it on hackster.io so others can use these tricks :slight_smile:

Hi @karimhamdy1. Thanks for the pointers. I’m working through the examples.

Thinking it through though I don’t think any tricks like that are going to help. What I need is for the application to wait until the pwmstart command has completed the “up” pulse of (e.g.) 1500us and the “down” pulse of 300us. It seems that the command (contrary to what I thought before) does block, but for the “up” pulse only, then continues execution at the end of the high pulse, thus effectively ignoring the “period” specification in the command. I guess this is only an issue when npulses = 1, presumably (I haven’t tried it) for npulses > 1 it will block until the last pulse has been generated, then return. For applications that need that kind of functionality it probably doesn’t matter that the function returns a bit earlier. But it does when npulses = 1. For my application anyway. It may be that for other applications all that is required is for the pin to go high for a specified time. Though it might be better to use a timer for that kind of function.

Meanwhile I’ve made things a bit cleaner by putting the time delay before the pwmstart, and using a timer period that is 300us less the number of microseconds between now and when the last pwmstart ended, so hopefully compensating for time spent in the rest of the program.

Still experimenting…

:wink:

Hi @karimhamdy1,

Thinking it through even more, I think using a systick timer is not optimal. It seems to me it is akin to using the delay() command in Arduino, which blocks the processor from doing anything for the delay timer period. It would be better to put it in a separate thread, but there doesn’t appear to be a “yield” function to pass control to another thread (there is one in vhal doc) so I don’t see how to return control to the processor (or another thread) as the function blocks, so even “yield” may not work. I may be able to do something trickier, like exiting the thread then every time it’s called it checks to see if the required time has elapsed, but I haven’t gone down that path.

In another thread I moaned about not being able to include “Arduino.h” in my C module in Zerynth, but I think I may have found a way around it, by copying large chunks of the Arduino library into the project. This has given me all the register definitions and register value definitions so I can use them in the C module. This allows me to set up the hardware PWM registers for the Due to create the output stream I want without interrupting the main processor at all! I’m very close to getting it to work. Just a couple of hiccups that I may need your assistance with… Some weird stuff happening that I don’t understand.

Thanks,
Ian

Hi @karimhamdy1, another question. I have not been able to get this thing to work, and I think the problem may be the way the ISR is defined. In normal C, the handler is defined as a normal function with a special name (PWM_Handler) that the compiler recognises as the ISR routine for all PWM events. However I think it may not be recognised by the compiler as an ISR when compiled under Zerynth as a called C module.
I have found reference to some vos functions that I think I may need to use to tell Zerynth that the function is an ISR. These are vosInstallHandler, vosEnterIsr and vosExitIsr. I tried a few experiments with these things but nothing worked. So, is there an example showing how to set up an ISR in a C module anywhere? Or some documentation about how to do it?

Thanks, Ian

For ISR in C, the ISR needs two special arguments.
Have a look at this C example for initializing and using interrupts in C.

Hi @karimhamdy1, thanks for the reply.
I have looked at the example but I don’t see how it is applicable for my program. It is specifically for detecting interrupts when a gpio pin changes state, using the vhalPinAttachInterrupt function. In my program the interrupt is triggered by a timer overflow, so everything is internal, there are no pins involved.
I also do not understand the function of the two parameters passed to the ISR; slot and dir. They are not referenced anywhere else. Does “slot” refer to the position in the NVIC interrupt table that points to the PWM interrupt? (“36” for the Arduino Due). “dir” I have no idea.
I tried a few variations of the code but nothing worked. Could you please provide an example of a timer interrupt, or point me to an explanation of what functions are required to be defined?
Thanks again,
Ian

Hi @karimhamdy1, I tried a few more random things to see if I could get anything to work. Among them I tried adding the two parameters to the ISR definition as in the above example, but this produced a compile error “conflicting types for ‘PWM_Handler’”. It seems “void PWM_Handler()” is the only definition that compiles.
:-\

You can register the irq with an associated ISR.
for instance, Try this in an initialization function in C :

vosInstallHandler(irq_number,ISR_function);
vhalIrqEnable(irq_number);

where irq_number is the irq number of the pwm, ISR_function is pointer to ISR function
The function should be triggered according to the irq.

Hi @karimhamdy1, thanks for the quick reply.

I tried your suggestion but couldn’t get it to work. The main reason, I have discovered is that my initialisation function starts but does not finish. I puta printf statement at the beginning, which appears in the output, and a printf statement at the end, which does not. So I thought, maybe there’s something in the routine that is hanging. I couldn’t see how that was possible, but as Sherlock Holmes once said “once you eliminate the impossible, whatever is left, however improbable, must be the case.” So I started putting printf statements all through it. I have now reached a point where I just don’t know what’s happening, and I need your expertise again. I’ve uploaded the whole project as a zip file as I think you will need all the files to investigate.

The “includes” folder contains all the definitions required for the register names and values in the C module, nothing startling there. I copied it from the Arduino libraries. The “main.py” is not very interesting either. The problem I have is in the ppmSetup.c module. It seems that the “initPWM” function is only partly executed. You will see a string of printf statements about half way down. By comparing to the screenshot of the output (below), you will see that not all of them are executed.
Arduino_Due_PWM_testing.zip (275.2 KB)

Screenshot from 2020-05-13 12-48-56

I’m completely bamboozled. Any suggestions as to how I can sort this out?

Thanks for your help,
Ian

EDIT 14 May.
I think I might be wasting your time @karimhamdy1. I just tried the same project in my other Linux Mint install, and the compile failed. It told me core_cm3.h was missing. So I tried it in my Linux Manjaro install. It failed with the same message. Now I have to figure out why it doesn’t fail in my primary Mint install. Or figure out what I’ve done to make it work…

Stay tuned.
:-\ Ian

Hi @karimhamdy1,

Well, been playing with this issue some more, and more confused than ever.
I have figured out that:

  1. I have to add some more header files to the “include” folder in the project to get the thing to compile. For some reason, in my daily drive Mint this doesn’t seem to be necessary, but in my other linuxes it is. Maybe there is an extra include path defined somehow, somewhere, don’t know…
  2. If I prevent the ppmsetup from actually starting the output signal generator on output pin (easy to do, just comment out the first register command) the program runs to completion and all the printf statements are executed. No pwm output stream is generated.
  3. If the setup for the pwm output stream is allowed to run, then a pwm output stream is generated, however the ISR (PWM_Handler) is never entered. The output consists of a series of pulses 2000us wide with a 300us separator, which is the default set in the original register settings. (CPRD2 and CDTY2). In this case the last printf command (and here 3…) is printed but the following one is not.

It seems that somehow, the PWM stream generation is taking over the cpu and not allowing anything else to run. So the rest of the program just stops. I can’t think of any other explanation, and yet it is diametrically opposite to my understanding of how the hardware pwm function works. I thought it ran in it’s own little piece of hardware, and did not require main mcu attention at all except to run the ISR when the interrupt was triggered. That’s certainly the case with the Atmel 328P 8 bit processors (Arduino Nano and Mega2560). I’ve done exactly the same thing with those and they run fine.

Not sure where to go from here. I think it is definitely not a Zerynth issue. It’s either an Arduino issue or an Atmel issue, and I’m leaning to the latter.

If you have some better ideas please let me know.

:disappointed: Ian

HI @mogplus8
Since you’re using the Atmel HAL libraries in addition to the standard functionalities of zerynth, debugging the errors is 10X harder, essentially you have to make sure that the libraries included and functionalities used do not mess up/contradict the underlying VOS layer and VHAL layer in Zerynth.
For instance, you are handling interrupts in your initialization of the PWM, disabling some interrupts and enabling them and using them.
using the HAL layer of the Atmel might/might not overwrite these settings, mess up some priority levels (especially when handling interrupt requests)…etc.
Of course, this is different than using an external driver library of a sensor, with the sensor library you are using Zerynth peripherals.

Back to your example, are you sure you are initializing the correct registers, handling them as stated in their SDK? Could you check the operation of each register, maybe their is some initialization that is needed before handling them, some clock configuration for instance.
If you are at dead end, Zerynth PWM is not enough, you can use external PWM IC or a cheap configuered 8-bit uC on PWM.

Hi @karimhamdy1,

Thanks for the ideas. Have to say I thought some of the vos/vhal functions were doing the same thing as the register manipulation was doing. If I remove them all the result is the same though. The “vosInstallHandler(irq_number,ISR_function);” is not required as “PWM_Handler” is already set as the default PWM interrupt handler. I tried printing the address of PWM_Handler, then using vosInstallHandler to set it to PWM_handler, and printed the address again. They were identical.

vhalIrqEnable(PWM_IRQn); does the same thing as NVIC_EnableIRQ(PWM_IRQn). If I use either (or both) it still works. “PWM_IRQn” is defined in the include libraries, and is “36”, which is the PWM’s slot in the interrupt table. (for the Due).

So neither of the Zerynth macros are required in this case, as the functions are already covered in the register manipulation.

I know the thing works. If I compile it as a straight C program it works perfectly. I can update the pulse widths in the main function and the new pulse widths are immediately reflected in the output stream. One interesting difference though is that in the C version I can update the channel count using the command “++chanCtr %= 9;” however in Zerynth this generates a compile error. Maybe Zerynth is using an older C compiler? Or different compile flags?

So,as it works as a C program, there is some kind of conflict created when Zerynth builds the module and links it in to the final program. So I’m now thinking, contrary to what I thought at 2am this morning, that it must be some kind of incompatibility between the Zerynth code and the C code, as it works fine when there is no Zerynth code involved.

I have no idea how to proceed from here. I am nowhere near techie enough to sort through Zerynth code and work out where the conflict is, and I’m sure the devs have far better things to do with their time than to look at this issue. It’s pretty unusual I’m guessing, and they have plenty of big ticket items to be getting on with. I can continue the development of the project (there’s lots more to do) and just ignore this part of it for now. I know there are some big changes coming up in Zerynth, and maybe one of them will be the ability to control the link process, so that precompiled modules (compiled in C, or any other language) can be natively linked in the to the Zerynth program without recompilation. That might solve my problem. I guess I’ll just have to hurry up and wait.

Thanks for all your help Karim, much appreciated.

:wink: Ian

Hi @karimhamdy1,
So I’ve been having another play with this problem (I know, like a dog with a bone, I can’t let it go) and have found some new stuff.
Most interestingly it seems the register names are available in the Python source code. I stumbled across this somehow, not sure how, anyway doesn’t matter. I found that if I put

REG_PMC_WPMR = 0

or

REG_PMC_WPMR = REG_PMC_WPMR | 0x01

into the Python source it compiles. It seems that Zerynth is smart enough to find the definition (which is a standard C style #define) and convert it to something that Zerynth Python can compile. However the constants that are used to manipulate the registers’ bits are not available. So

REG_PMC_WPMR = REG_PMC_WPMR | PMC_WPMR_WPEN

does not compile (note the standard C construct " |= " construct doesn’t work in Python) because PMC_WPMR_WPEN is not defined. Not a problem, I can define them myself in the Python source.

Only minor fly in the ointment now is that there is one definition missing, REG_PWM_ISR1. This register must be cleared (by reading it) in the ISR, otherwise things don’t work. I don’t know how to define this field in Python as the register must be pointed to by a variable containing the address of the register. As far as I am aware Python cannot manipulate data at a specific hardware address as it does not have a C like pointer function.

With a bit of detective work I found what I thought to be the library where Zerynth was getting the register names from (there is only one library in .zerynth2 that contains REG_PMC_WPMR), but when I tried adding another definition to it (to verify that I had the right library) my new definition was not picked up. I tried restarting Zerynth but still no luck. So I have no idea where Zerynth is getting the definitions from. Anyway the missing register name was defined in that file, so it couldn’t have been the one Zerynth is using.

So, couple of questions. 1) Where is Zerynth getting the definitions from? 2) is it possible to get Zerynth to use other libraries (e.g. the library the source code is in, or a sub library of it) to source its definitions? If so how can this be done.

Hard questions I know, so I’m not expecting miracles…

Thanks, Ian

EDIT
Maybe my detective work was not so thorough. I think I’ve really found where they are now, in vhal/armcmx/atmelsam3x in vhal_common.h and vhal_tim.h (and vhal_tim.c). All the register names are defined in there, although for some reason some of them are resolved when I put them into a Zerynth program, and some are not. The ones defined in vhal_tim.h that are resolved are also referenced in vhal_tim.c, and the ones that aren’t referenced there are not resolved, so maybe that’s the difference. Somewhere the string “REG_” is prefixed to them too, no idea where that happens. From my very brief perusal of the files in these libraries they appear to be a work in progress as there are what appear to be debugging print commands scattered around them. So maybe I shouldn’t be playing with them as I have no idea what I’m doing…

Anyway I think I may have answered one of my questions. I think though that this line of experimentation is going to be (if it isn’t already) another dead end.

:wink: Ian