Skip to content
This repository has been archived by the owner on Nov 28, 2022. It is now read-only.

Adding low level buffer operations ro RPIO.PWM #20

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions source/RPIO/PWM/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,21 @@ def add_channel_pulse(dma_channel, gpio, start, width):
"""
return _PWM.add_channel_pulse(dma_channel, gpio, start, width)

def buffer_set_on(dma_channel, position):
"""Write an on instruction to the buffer at the specified position"""
return _PWM.buffer_set_on(dma_channel, position)

def buffer_set_off(dma_channel, position):
"""Write an off instruction to the buffer at the specified position"""
return _PWM.buffer_set_off(dma_channel, position)

def buffer_assign(dma_channel, gpio, position):
"""Assign a gpio channel to a specific point in the buffer"""
return _PWM.buffer_assign(dma_channel, gpio, position)

def buffer_unassign(dma_channel, gpio, position):
"""Unassign a gpio channel to a specific point in the buffer"""
return _PWM.buffer_unassign(dma_channel, gpio, position)

def print_channel(channel):
""" Print info about a specific channel to stdout """
Expand Down
104 changes: 95 additions & 9 deletions source/c_pwm/pwm.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@
* PULSE WIDTH INCREMENT GRANULARITY
* ---------------------------------
* Another very important setting is the pulse width increment granularity, which
* defaults to 10�s and is used for _all_ DMA channels (since its passed to the PWM
* defaults to 10µs and is used for _all_ DMA channels (since its passed to the PWM
* timing hardware). Under the hood you need to set the pulse widths as multiples
* of the increment-granularity. Eg. in order to set 500�s pulses with a granularity
* setting of 10�s, you'll need to set the pulse-width as 50 (50 * 10�s = 500�s).
* of the increment-granularity. Eg. in order to set 500µs pulses with a granularity
* setting of 10µs, you'll need to set the pulse-width as 50 (50 * 10µs = 500µs).
* Less granularity needs more DMA memory.
*
* To achieve shorter pulses than 10�s, you simply need set a lower granularity.
* To achieve shorter pulses than 10µs, you simply need set a lower granularity.
*
*
* WARNING
Expand All @@ -84,6 +84,7 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

#include "pwm.h"

// 15 DMA channels are usable on the RPi (0..14)
Expand Down Expand Up @@ -468,19 +469,104 @@ add_channel_pulse(int channel, int gpio, int width_start, int width)
*(dp + width_start) |= 1 << gpio;
cbp->dst = phys_gpset0;

// Do nothing for the specified width
for (i = 1; i < width - 1; i++) {
*(dp + width_start + i) &= ~(1 << gpio); // set just this gpio's bit to 0
cbp += 2;
}
//PaulP - re-order to write the off code before clearing the mid region.
cbp += 2 * (width - 2);

// Clear GPIO at end
*(dp + width_start + width) |= 1 << gpio;
cbp->dst = phys_gpclr0;

//PaulP - force a cache flush so this is in the DMA chain now.
__clear_cache((char*) (dp + width_start + width), (char*) (dp + width_start + width + 1));
__clear_cache((char*) (cbp), (char*) (cbp + 2));

// Do nothing for the specified width
for (i = 1; i < width - 1; i++) {
*(dp + width_start + i) &= ~(1 << gpio); // set just this gpio's bit to 0
}

return EXIT_SUCCESS;
}

//Write an 'on' instruction to the buffer at the specified position.
int
buffer_set_on(int channel, int position)
{
static uint32_t phys_gpset0 = 0x7e200000 + 0x1c;

//log_debug("buffer_set_on: channel=%d, position=%d\n", channel, position);
if (!channels[channel].virtbase)
return fatal("Error: channel %d has not been initialized with 'init_channel(..)'\n", channel);
if (position > channels[channel].width_max || position < 0)
return fatal("Error: position exceeds max_width of %d\n", channels[channel].width_max);

dma_cb_t *cbp = (dma_cb_t *) get_cb(channel) + (position * 2);
cbp->dst = phys_gpset0;
__clear_cache((char*) cbp, (char*) (cbp + 2));

return EXIT_SUCCESS;
}

//Write an 'off' instruction to the buffer at the specified position.
int
buffer_set_off(int channel, int position)
{
static uint32_t phys_gpclr0 = 0x7e200000 + 0x28;

//log_debug("buffer_set_on: channel=%d, position=%d\n", channel, position);
if (!channels[channel].virtbase)
return fatal("Error: channel %d has not been initialized with 'init_channel(..)'\n", channel);
if (position > channels[channel].width_max || position < 0)
return fatal("Error: position exceeds max_width of %d\n", channels[channel].width_max);

dma_cb_t *cbp = (dma_cb_t *) get_cb(channel) + (position * 2);
cbp->dst = phys_gpclr0;
__clear_cache((char*) cbp, (char*) (cbp + 2));

return EXIT_SUCCESS;
}

//Assign a gpio channel to a specific point in the buffer
int
buffer_assign(int channel, int gpio, int position)
{
uint32_t *dp = (uint32_t *) channels[channel].virtbase;

//log_debug("buffer_assign: channel=%d, gpio=%d, position=%d\n", channel, gpio, position);
if (!channels[channel].virtbase)
return fatal("Error: channel %d has not been initialized with 'init_channel(..)'\n", channel);
if (position > channels[channel].width_max || position < 0)
return fatal("Error: position exceeds max_width of %d\n", channels[channel].width_max);

if ((gpio_setup & 1<<gpio) == 0)
init_gpio(gpio);

*(dp + position) |= 1 << gpio;
__clear_cache((char*) (dp + position), (char*) (dp + position + 1));

return EXIT_SUCCESS;
}

//Unassign a gpio channel to a specific point in the buffer
int
buffer_unassign(int channel, int gpio, int position)
{
uint32_t *dp = (uint32_t *) channels[channel].virtbase;

//log_debug("buffer_unassign: channel=%d, gpio=%d, position=%d\n", channel, gpio, position);
if (!channels[channel].virtbase)
return fatal("Error: channel %d has not been initialized with 'init_channel(..)'\n", channel);
if (position > channels[channel].width_max || position < 0)
return fatal("Error: position exceeds max_width of %d\n", channels[channel].width_max);

if ((gpio_setup & 1<<gpio) == 0)
init_gpio(gpio);

*(dp + position) &= ~(1 << gpio);
__clear_cache((char*) (dp + position), (char*) (dp + position + 1));

return EXIT_SUCCESS;
}

// Get a channel's pagemap
static int
Expand Down
5 changes: 5 additions & 0 deletions source/c_pwm/pwm.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ int clear_channel_gpio(int channel, int gpio);
int print_channel(int channel);

int add_channel_pulse(int channel, int gpio, int width_start, int width);
int buffer_set_on(int channel, int position);
int buffer_set_off(int channel, int position);
int buffer_assign(int channel, int gpio, int position);
int buffer_unassign(int channel, int gpio, int position);

char* get_error_message(void);
void set_softfatal(int enabled);

Expand Down
70 changes: 69 additions & 1 deletion source/c_pwm/pwm_py.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,70 @@ py_add_channel_pulse(PyObject *self, PyObject *args)
return Py_None;
}

// python function (void) buffer_set_on(int channel, int position)
static PyObject*
py_buffer_set_on(PyObject *self, PyObject *args)
{
int channel, position;

if (!PyArg_ParseTuple(args, "ii", &channel, &position))
return NULL;

if (buffer_set_on(channel, position) == EXIT_FAILURE)
return raise_error();

Py_INCREF(Py_None);
return Py_None;
}

// python function (void) buffer_set_off(int channel, int position)
static PyObject*
py_buffer_set_off(PyObject *self, PyObject *args)
{
int channel, position;

if (!PyArg_ParseTuple(args, "ii", &channel, &position))
return NULL;

if (buffer_set_off(channel, position) == EXIT_FAILURE)
return raise_error();

Py_INCREF(Py_None);
return Py_None;
}

// python function (void) buffer_assign(int channel, int gpio, int position)
static PyObject*
py_buffer_assign(PyObject *self, PyObject *args)
{
int channel, gpio, position;

if (!PyArg_ParseTuple(args, "iii", &channel, &gpio, &position))
return NULL;

if (buffer_assign(channel, gpio, position) == EXIT_FAILURE)
return raise_error();

Py_INCREF(Py_None);
return Py_None;
}

// python function (void) buffer_assign(int channel, int gpio, int position)
static PyObject*
py_buffer_unassign(PyObject *self, PyObject *args)
{
int channel, gpio, position;

if (!PyArg_ParseTuple(args, "iii", &channel, &gpio, &position))
return NULL;

if (buffer_unassign(channel, gpio, position) == EXIT_FAILURE)
return raise_error();

Py_INCREF(Py_None);
return Py_None;
}

// python function print_channel(int channel)
static PyObject*
py_print_channel(PyObject *self, PyObject *args)
Expand Down Expand Up @@ -205,7 +269,11 @@ static PyMethodDef pwm_methods[] = {
{"init_channel", py_init_channel, METH_VARARGS, "Setup a channel with a specific period time and hardware"},
{"clear_channel", py_clear_channel, METH_VARARGS, "Clear all pulses on this channel"},
{"clear_channel_gpio", py_clear_channel_gpio, METH_VARARGS, "Clear one specific GPIO from this channel"},
{"add_channel_pulse", py_add_channel_pulse, METH_VARARGS, "Add a specific pulse to a channel"},
{"add_channel_pulse", py_add_channel_pulse, METH_VARARGS, "Add a specific pulse to a channel"},
{"buffer_set_on", py_buffer_set_on, METH_VARARGS, "Write an on instruction to the buffer at the specified position"},
{"buffer_set_off", py_buffer_set_off, METH_VARARGS, "Write an off instruction to the buffer at the specified position"},
{"buffer_assign", py_buffer_assign, METH_VARARGS, "Assign a gpio channel to a specific point in the buffer"},
{"buffer_unassign", py_buffer_unassign, METH_VARARGS, "Unassign a gpio channel to a specific point in the buffer"},
{"print_channel", py_print_channel, METH_VARARGS, "Print info about a specific channel"},
{"set_loglevel", py_set_loglevel, METH_VARARGS, "Set the loglevel to either 0 (debug) or 1 (errors)"},
{"is_setup", py_is_setup, METH_VARARGS, "Returns 1 is setup(..) has been called, else 0"},
Expand Down