EMC2 running on Raspberry Pi?

More
22 Apr 2013 21:10 #33000 by andypugh

mhaberler wrote: Not a good analogy - the Mesa 'base thread' is on the FPGA so that path doesnt need a host-side base thread


Ah, OK. For some reason I thought this was a scheme where a "waveform" was loaded into a buffer and then automatically written out. I don't know where I got that idea from.

Please Log in or Create an account to join the conversation.

More
22 Apr 2013 22:46 - 22 Apr 2013 22:48 #33008 by PCW
Replied by PCW on topic EMC2 running on Raspberry Pi?
Thats was what I envisioned:
DMA continuously playing out a set of prerecorded step/dir 'tracks' from memory at some reasonable (and constant) rate, from a circular RAM buffer.
The 'base thread' rate is DMA rate in this instance.

The servo threads job here is to lay down the step/dir tracks ahead of the DMA 'player' so for example at a 5 KHz servo thread and 500 KHz DMA rate, ~100 memory locations would need to be updated every servo thread invocation.

One of the interesting things is that the stepgen comp should be able to be used almost as is.
its 'base thread' is simply called every time the 100 + memory locations are written. Basically this is just time base correction using the DMA to deliver the output data at a constant rate.
Last edit: 22 Apr 2013 22:48 by PCW.

Please Log in or Create an account to join the conversation.

More
22 Apr 2013 23:29 #33009 by mungkie

mhaberler wrote:

andypugh wrote:

mungkie wrote: All stepgen functions will run at servo_thread period INCLUDING MAKE_PULSES!!!!!!!!!,

That seems right, as it is also the case with a Mesa stepgen.

Not a good analogy - the Mesa 'base thread' is on the FPGA so that path doesnt need a host-side base thread

see here: www.linuxcnc.org/docs/devel/html/man/man9/stepgen.9.html :

stepgen.make-pulses <---- this one should be on the base thread, i.e fast

stepgen.capture-position and stepgen.update-freq can be on the servo thread

you wont get far with make-pulses on the servo thread

- Michael


I am not sure you are correct, the whole idea of using the dma system is so that we do not need a fast base thread.

When make-pulses is called in servo thread it loops over the step creation loop 100 times (or whatever required) to generate the gpio pulse bit_patterns required by the dma buffer that writes to the gpio controlled by the pwm clock (i.e. actual gpio writing and timing is off loaded to dma program written in dma control block code).

Of course the step_gen could be run in the base thread and change the base thread to the same period as the servo thread.?

My problem is I am not sure on how linuxcnc handles things, I am starting to get the feeling I do not understand anything properly, and reading though all the linuxcnc code could take weeks to understand it.

Also the code I posted has some terrible bugs and when I tried running it yesterday it did not work at all, first thought was that I used a bitwise inversion when I should have used logical ( ~ when I must use ! i.e. current_buffer= ~current_buffer; ). but the code still does not work and I think this may be due to a deadlock when dma trys to access memory at the same time as the make-pulses code??

I really have no idea.


I think I need to add some extra dma control blocks in init_ctrl_data() at the start and halfway through the dma control block loop that write to the current_buffer (current buffer is just a flag to say which half of the buffer is currently being used by the dma program) memory so that make pulses knows which half of the buffer to write to.?

I am not sure what is going on with the code, and testing is not easy as I have only been able to guess what the gpio is doing by connecting a multimeter frequency meter to the gpio pin for testing.

I will have a bit more of a play with things tonight.

Please Log in or Create an account to join the conversation.

More
22 Apr 2013 23:36 #33010 by mhaberler
oops, I guess I blundered in without reading back all the way

sorry -m

Please Log in or Create an account to join the conversation.

More
24 Apr 2013 01:48 #33082 by mungkie
I spent most my time last night trying to get an old oscilloscope to work, unfortunatly the trig does not seem to be syncing so I think the information about waveforms that I have could be wrong.

I think I have fixed some of the problems with the code, but not sure if there is a problem with the code or with the oscilloscope now.

Is there anyone here with a raspberry pi and a DSO that could run some tests??

I will post the code for a basic command line test of the system, all you will need is a rpi running raspbian a DSO and an 1/2 hour to compile the code plug things together and collate the results and post them back here.

From the oscilloscope I have it looks like the pulse generation is very good but then the pulses stop for a period and restart, I guesssed it could be problems with the test equipment triggernig , I know there are definitly faults with it since I got it off ebay, maybe should continue reviewing the code before asking others to do tests???


Anyone have any suggestions??

Also what sort of output wave should the step_gen pins have, I have programed the code so the pin toggles (I.e. always low unless a pulse is sent), i imagine some systems may require set/clr (i.e. once pulse is sent output pin stays at the set level until it is changed)

Please Log in or Create an account to join the conversation.

More
24 Apr 2013 05:47 #33099 by andypugh

mungkie wrote: Is there anyone here with a raspberry pi and a DSO that could run some tests??

I do have both, though I have barely even booted the Pi.
Having just done so, it seems to be running Raspbian.

ll you will need is a rpi running raspbian a DSO and an 1/2 hour to compile the code plug things together and collate the results and post them back here.

And a monitor and scope in the same room :-)

Also what sort of output wave should the step_gen pins have, I have programed the code so the pin toggles (I.e. always low unless a pulse is sent), i imagine some systems may require set/clr (i.e. once pulse is sent output pin stays at the set level until it is changed)

I don't think any need that. Supporting Quadrature might be nice, and actually supporting all the patterns in the standard stepgen would be great. Some people are going to want to invert the sense of the pin (high most of the time, dipping low for the pulse length).

Please Log in or Create an account to join the conversation.

More
24 Apr 2013 22:33 #33149 by mungkie
If you can copy the code below to a textfile on your rpi (e.g. dma_stepbuf.c ) then at the command prompt in the directory where the file is type 'gcc dma_stepbuf.c'

If there are any errors either you or I have made a mistake somewhere so post the errors here I will try to solve them.

If the code compiles without errors connect the DSO to pins 22,23,24 of the P1 gpio port on the rpi, then type 'sudo ./a.out'

The code should printout a list of the bit patterns on gpio pins 22/23/24 and let the DMA loop for 30 seconds then change the bit patterns.

What I would really like to know from the test is if there is any delay at the begining middle or end of the 200 sample buffer when the DMA loops back to the start of the buffer or changes the buffer position flag.

If the step generation looks fairly constant on the DSO then I will try and finish off the driver for linuxcnc (should only take an evenings work unless I have missed something).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdint.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/mman.h>

int setup(int pw_incr_us);
void shutdown(void);
int init_channel(int channel, int subcycle_time_us);
int print_channel(int channel);

#define SUBCYCLE_TIME_US_DEFAULT 4000

// Subcycle minimum. We kept seeing no signals and strange behavior of the RPi
#define SUBCYCLE_TIME_US_MIN 2000

// Default pulse-width-increment-granularity
#define PULSE_WIDTH_INCREMENT_GRANULARITY_US_DEFAULT 20

// 15 DMA channels are usable on the RPi (0..14)
#define DMA_CHANNELS    15

// Standard page sizes
#define PAGE_SIZE       4096
#define PAGE_SHIFT      12

// Memory Addresses
#define DMA_BASE        0x20007000
#define DMA_CHANNEL_INC 0x100
#define DMA_LEN         0x24
#define PWM_BASE        0x2020C000
#define PWM_LEN         0x28
#define CLK_BASE        0x20101000
#define CLK_LEN         0xA8
#define GPIO_BASE       0x20200000
#define GPIO_LEN        0x100

// Datasheet p. 51:
#define DMA_NO_WIDE_BURSTS  (1<<26)
#define DMA_WAIT_RESP   (1<<3)
#define DMA_D_DREQ      (1<<6)
#define DMA_PER_MAP(x)  ((x)<<16)
#define DMA_END         (1<<1)
#define DMA_RESET       (1<<31)
#define DMA_INT         (1<<2)

// Each DMA channel has 3 writeable registers:
#define DMA_CS          (0x00/4)
#define DMA_CONBLK_AD   (0x04/4)
#define DMA_DEBUG       (0x20/4)

// GPIO Memory Addresses
#define GPIO_FSEL0      (0x00/4)
#define GPIO_SET0       (0x1c/4)
#define GPIO_CLR0       (0x28/4)
#define GPIO_LEV0       (0x34/4)
#define GPIO_PULLEN     (0x94/4)
#define GPIO_PULLCLK    (0x98/4)

// GPIO Modes (IN=0, OUT=1)
#define GPIO_MODE_IN    0
#define GPIO_MODE_OUT   1

// PWM Memory Addresses
#define PWM_CTL         (0x00/4)
#define PWM_DMAC        (0x08/4)
#define PWM_RNG1        (0x10/4)
#define PWM_FIFO        (0x18/4)
#define PWMCLK_CNTL     40
#define PWMCLK_DIV      41
#define PWMCTL_MODE1    (1<<1)
#define PWMCTL_PWEN1    (1<<0)
#define PWMCTL_CLRF     (1<<6)
#define PWMCTL_USEF1    (1<<5)
#define PWMDMAC_ENAB    (1<<31)
#define PWMDMAC_THRSHLD ((15<<8) | (15<<0))

// DMA Control Block Data Structure (p40): 8 words (256 bits)
typedef struct {
    uint32_t info;   // TI: transfer information
    uint32_t src;    // SOURCE_AD
    uint32_t dst;    // DEST_AD
    uint32_t length; // TXFR_LEN: transfer length
    uint32_t stride; // 2D stride mode
    uint32_t next;   // NEXTCONBK
    uint32_t pad[2]; // _reserved_
} dma_cb_t;

// Memory mapping
typedef struct {
    uint8_t *virtaddr;
    uint32_t physaddr;
} page_map_t;

// Main control structure per channel
struct channel {
    uint8_t *virtbase;
    uint32_t *sample;
    dma_cb_t *cb;
    page_map_t *page_map;
    volatile uint32_t *dma_reg;

    // Set by user
    uint32_t subcycle_time_us;
    uint32_t num_flags;
    // Set by system
    uint32_t num_samples;
    uint32_t num_cbs;
    uint32_t num_pages;

    // Used only for control purposes
    uint32_t width_max;
};

// One control structure per channel
static struct channel channels[DMA_CHANNELS];

// Pulse width increment granularity
static uint16_t pulse_width_incr_us = -1;
static uint8_t _is_setup = 0;
static int gpio_setup = 0; // bitfield for setup gpios (setup = out/low)

// Common registers
static volatile uint32_t *pwm_reg;
static volatile uint32_t *pcm_reg;
static volatile uint32_t *clk_reg;
static volatile uint32_t *gpio_reg;

#define log_debug printf

// Sets a GPIO to either GPIO_MODE_IN(=0) or GPIO_MODE_OUT(=1)
static void gpio_set_mode(uint32_t pin, uint32_t mode)
{
    uint32_t fsel = gpio_reg[GPIO_FSEL0 + pin/10];

    fsel &= ~(7 << ((pin % 10) * 3));
    fsel |= mode << ((pin % 10) * 3);
    gpio_reg[GPIO_FSEL0 + pin/10] = fsel;
}


static void udelay(int us)
{	struct timespec ts = { 0, us * 1000 };	nanosleep(&ts, NULL);	}

// Shutdown -- its important to reset the DMA before quitting
void shutdown(void)
{
    int i;

    for (i = 0; i < DMA_CHANNELS; i++) {
        if (channels[i].dma_reg && channels[i].virtbase) {
            log_debug("shutting down dma channel %d\n", i);
            udelay(channels[i].subcycle_time_us);
            channels[i].dma_reg[DMA_CS] = DMA_RESET;
            udelay(10);
        }
    }
}

// Terminate is triggered by signals
static void terminate(void)
{
    shutdown();
    exit(0);
}

#define fatal(fmt...) { printf(fmt);     shutdown();    exit(-1); }

// Catch all signals possible - it is vital we kill the DMA engine on process exit!
static void setup_sighandlers(void)
{
    int i;
    for (i = 0; i < 64; i++) {
        struct sigaction sa;
        memset(&sa, 0, sizeof(sa));
        sa.sa_handler = (void *) terminate;
        sigaction(i, &sa, NULL);
    }
}

// Memory mapping
static uint32_t mem_virt_to_phys(int channel, void *virt)
{
    uint32_t offset = (uint8_t *)virt - channels[channel].virtbase;
    return channels[channel].page_map[offset >> PAGE_SHIFT].physaddr + (offset % PAGE_SIZE);
}

// Peripherals memory mapping
static void * map_peripheral(uint32_t base, uint32_t len)
{
    int fd = open("/dev/mem", O_RDWR);
    void * vaddr;

    if (fd < 0) {
        fatal("rpio-pwm: Failed to open /dev/mem: %m\n");
        return NULL;
    }
    vaddr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, base);
    if (vaddr == MAP_FAILED) {
        fatal("rpio-pwm: Failed to map peripheral at 0x%08x: %m\n", base);
        return NULL;
    }
    close(fd);

    return vaddr;
}

// Returns a pointer to the control block of this channel in DMA memory
uint8_t* get_cb(int channel)
{
    return channels[channel].virtbase + (sizeof(uint32_t)) * channels[channel].num_samples + (sizeof(uint32_t)) * channels[channel].num_flags ;
}

// Get a channel's pagemap
static int make_pagemap(int channel)
{
    int i, fd, memfd, pid;
    char pagemap_fn[64];

    channels[channel].page_map = malloc(channels[channel].num_pages * sizeof(*channels[channel].page_map));

    if (channels[channel].page_map == 0)	fatal("rpio-pwm: Failed to malloc page_map: %m\n");

    memfd = open("/dev/mem", O_RDWR);
    if (memfd < 0)	fatal("rpio-pwm: Failed to open /dev/mem: %m\n");
    pid = getpid();
    sprintf(pagemap_fn, "/proc/%d/pagemap", pid);
    fd = open(pagemap_fn, O_RDONLY);
    if (fd < 0)	fatal("rpio-pwm: Failed to open %s: %m\n", pagemap_fn);
    if (lseek(fd, (uint32_t)channels[channel].virtbase >> 9, SEEK_SET) !=
                        (uint32_t)channels[channel].virtbase >> 9) {
	fatal("rpio-pwm: Failed to seek on %s: %m\n", pagemap_fn);
    }
    for (i = 0; i < channels[channel].num_pages; i++) {
        uint64_t pfn;
        channels[channel].page_map[i].virtaddr = channels[channel].virtbase + i * PAGE_SIZE;
        // Following line forces page to be allocated
        channels[channel].page_map[i].virtaddr[0] = 0;
        if (read(fd, &pfn, sizeof(pfn)) != sizeof(pfn))	fatal("rpio-pwm: Failed to read %s: %m\n", pagemap_fn);
        if (((pfn >> 55) & 0x1bf) != 0x10c)	fatal("rpio-pwm: Page %d not present (pfn 0x%016llx)\n", i, pfn);
        channels[channel].page_map[i].physaddr = (uint32_t)pfn << PAGE_SHIFT | 0x40000000;
    }
    close(fd);
    close(memfd);
    return 0;
}

static int init_virtbase(int channel)
{
    channels[channel].virtbase = mmap(NULL, channels[channel].num_pages * PAGE_SIZE, PROT_READ|PROT_WRITE,
            MAP_SHARED|MAP_ANONYMOUS|MAP_NORESERVE|MAP_LOCKED, -1, 0);
    if (channels[channel].virtbase == MAP_FAILED)	fatal("rpio-pwm: Failed to mmap physical pages: %m\n");
    if ((unsigned long)channels[channel].virtbase & (PAGE_SIZE-1))	fatal("rpio-pwm: Virtual address is not page aligned\n");
    return 0;
}

// Initialize control block for this channel
static int init_ctrl_data(int channel)
{
    dma_cb_t *cbp = (dma_cb_t *) get_cb(channel);
    uint32_t *sample = (uint32_t *) channels[channel].virtbase;
    uint32_t phys_gpset0 = 0x7e200000 + 0x1c;
    uint32_t phys_fifo_addr;
    uint32_t phys_gpclr0 = 0x7e200000 + 0x28;
    int i;

    channels[channel].dma_reg = map_peripheral(DMA_BASE, DMA_LEN) + (DMA_CHANNEL_INC * channel);
    if (channels[channel].dma_reg == NULL)
        return -1;

        phys_fifo_addr = (PWM_BASE | 0x7e000000) + 0x18;

    // Reset complete per-sample gpio mask to 0
    memset(sample, 0, sizeof(channels[channel].num_samples * sizeof(uint32_t)));

#define Setcbbp() cbp->length = 4; cbp->stride = 0; cbp->next = mem_virt_to_phys(channel, cbp + 1); cbp++;

#define FLAG_BUF 0

#if FLAG_BUF

// add a control block at start to flag writing this half of buffer

	cbp->info = DMA_NO_WIDE_BURSTS;
	cbp->src = mem_virt_to_phys(channel, sample + channels[channel].num_samples+1);
	cbp->dst = mem_virt_to_phys(channel, sample + channels[channel].num_samples);
	Setcbbp();

	cbp->info = DMA_NO_WIDE_BURSTS;
	cbp->src = mem_virt_to_phys(channel, sample + channels[channel].num_samples+1);
	cbp->dst = mem_virt_to_phys(channel, sample + channels[channel].num_samples);
	Setcbbp();

#endif

    for (i = 0; i < channels[channel].num_samples; i++) {

        cbp->info = DMA_NO_WIDE_BURSTS | DMA_WAIT_RESP;
        cbp->src = mem_virt_to_phys(channel, sample + i);
        cbp->dst = phys_gpclr0;
	Setcbbp();

       // Delay
	cbp->info = DMA_NO_WIDE_BURSTS | DMA_WAIT_RESP | DMA_D_DREQ | DMA_PER_MAP(5);
	cbp->src = mem_virt_to_phys(channel, sample); // Any data will do
	cbp->dst = phys_fifo_addr;
	Setcbbp();


#if FLAG_BUF
// add an extra flag control block to show writing top half of buffer
if(i == channels[channel].num_samples/2 )
{
	cbp->info = DMA_NO_WIDE_BURSTS;
	cbp->src = mem_virt_to_phys(channel, sample + channels[channel].num_samples+2);
	cbp->dst = mem_virt_to_phys(channel, sample + channels[channel].num_samples);
	Setcbbp();

	cbp->info = DMA_NO_WIDE_BURSTS;
	cbp->src = mem_virt_to_phys(channel, sample + channels[channel].num_samples+2);
	cbp->dst = mem_virt_to_phys(channel, sample + channels[channel].num_samples);
	Setcbbp();

}
#endif

	cbp->info = DMA_NO_WIDE_BURSTS | DMA_WAIT_RESP;
	cbp->src = mem_virt_to_phys(channel, sample + i);  // src contains mask of which gpios need change at this sample
	cbp->dst = phys_gpset0; //phys_gpclr0;  // set each sample to clear set gpios by default
	Setcbbp();

        // Delay
	cbp->info = DMA_NO_WIDE_BURSTS | DMA_WAIT_RESP | DMA_D_DREQ | DMA_PER_MAP(5);
	cbp->src = mem_virt_to_phys(channel, sample); // Any data will do
	cbp->dst = phys_fifo_addr;
	Setcbbp();
}

    // The last control block links back to the first (= endless loop)
    cbp--;
    cbp->next = mem_virt_to_phys(channel, get_cb(channel));

    // Initialize the DMA channel 0 (p46, 47)
    channels[channel].dma_reg[DMA_CS] = DMA_RESET; // DMA channel reset
    udelay(10);
    channels[channel].dma_reg[DMA_CS] = DMA_INT | DMA_END; // Interrupt status & DMA end flag
    channels[channel].dma_reg[DMA_CONBLK_AD] = mem_virt_to_phys(channel, get_cb(channel));  // initial CB
    channels[channel].dma_reg[DMA_DEBUG] = 7; // clear debug error flags
    channels[channel].dma_reg[DMA_CS] = 0x10880001;    // go, mid priority, wait for outstanding writes

    return 0;
}

// Initialize PWM or PCM hardware once for all channels (10MHz)
static void init_hardware(void)
{
        // Initialise PWM
        pwm_reg[PWM_CTL] = 0;
        udelay(10);
        clk_reg[PWMCLK_CNTL] = 0x5A000006;        // Source=PLLD (500MHz)
        udelay(100);
        clk_reg[PWMCLK_DIV] = 0x5A000000 | (50<<12);    // set pwm div to 50, giving 10MHz
        udelay(100);
        clk_reg[PWMCLK_CNTL] = 0x5A000016;        // Source=PLLD and enable
        udelay(100);
        pwm_reg[PWM_RNG1] = pulse_width_incr_us * 10;
        udelay(10);
        pwm_reg[PWM_DMAC] = PWMDMAC_ENAB | PWMDMAC_THRSHLD;
        udelay(10);
        pwm_reg[PWM_CTL] = PWMCTL_CLRF;
        udelay(10);
        pwm_reg[PWM_CTL] = PWMCTL_USEF1 | PWMCTL_PWEN1;
        udelay(10);
}

int init_channel(int channel, int subcycle_time_us)
{
    log_debug("Initializing channel %d...\n", channel);
    if (_is_setup == 0)	fatal("Error: you need to call `setup(..)` before initializing channels\n");
    if (channel > DMA_CHANNELS-1)	fatal("Error: maximum channel is %d (requested channel %d)\n", DMA_CHANNELS-1, channel);

    // Setup Data
    channels[channel].num_flags = 8;
    channels[channel].subcycle_time_us = subcycle_time_us;
    channels[channel].num_samples = channels[channel].subcycle_time_us / pulse_width_incr_us;
    channels[channel].width_max = channels[channel].num_samples - 1;
    channels[channel].num_cbs = (channels[channel].num_samples * 4) + 4;
    channels[channel].num_pages = ((channels[channel].num_cbs * 32 + channels[channel].num_samples * 4 + channels[channel].num_flags * 4 + \
                                       PAGE_SIZE - 1) >> PAGE_SHIFT);

    // Initialize channel
    if (init_virtbase(channel) == -1)	return -1;
    if (make_pagemap(channel) == -1)	return -1;
    if (init_ctrl_data(channel) == -1)	return -1;
    return 0;
}


int print_channel(int channel)
{
    if (channel > DMA_CHANNELS - 1)
fatal("Error: you tried to print channel %d, but max channel is %d\n", channel, DMA_CHANNELS-1);
    log_debug("Subcycle time: %dus\n", channels[channel].subcycle_time_us);
    log_debug("PW Increments: %dus\n", pulse_width_incr_us);
    log_debug("Num samples:   %d\n", channels[channel].num_samples);
    log_debug("Num CBS:       %d\n", channels[channel].num_cbs);
    log_debug("Num pages:     %d\n", channels[channel].num_pages);
    return 0;
}


int setup(int pw_incr_us)
{
	pulse_width_incr_us = pw_incr_us;

	if (_is_setup == 1)	fatal("Error: setup(..) has already been called before\n");
	log_debug("PW increments:  %dus\n", pulse_width_incr_us);
	setup_sighandlers();

	pwm_reg = map_peripheral(PWM_BASE, PWM_LEN);

	clk_reg = map_peripheral(CLK_BASE, CLK_LEN);
	gpio_reg = map_peripheral(GPIO_BASE, GPIO_LEN);
	if (pwm_reg == NULL || clk_reg == NULL || gpio_reg == NULL)	return -1;

	init_hardware();

	_is_setup = 1;
	return 0;
}

int freq(int channel, int gpio,  int width)
{
	int i;
	uint32_t phys_gpclr0 = 0x7e200000 + 0x28;
	uint32_t phys_gpset0 = 0x7e200000 + 0x1c;
	uint32_t *dp = (uint32_t *) channels[channel].virtbase;

// this is for the buffer sync position flags...
*(dp + channels[channel].num_samples+1) = 1;
*(dp + channels[channel].num_samples+2) = 0;

	log_debug("freq: channel=%d, gpio=%d, width=%d\n", 
	channel, gpio, width);

    if ((gpio_setup & 1<<gpio) == 0){
	log_debug("init_gpio %d\n", gpio);
	gpio_reg[GPIO_CLR0] = 1 << gpio;
	gpio_set_mode(gpio, GPIO_MODE_OUT);
	gpio_setup |= 1 << gpio;
	}

{
int j=0, oon=1;
for (i = 0; i < channels[channel].num_samples ; i++) {
if(j== width){ j=0;  oon= !oon;  }     
if(oon){ *(dp + i) &= ~(1 << gpio);  }	// set 0	
else{	*(dp + i) |= 1 << gpio;	}	// set 1	
j++;
}
}
	return 0;
}

// prints out bit patterns sent to gpio 22/23/24
int print_buf(int channel)
{
	int i;
	uint32_t *dp = (uint32_t *) channels[channel].virtbase;

	log_debug("buffer: \n");
    for (i = 0; i < channels[channel].num_samples ; i++) {
 log_debug("%d%d%d\n", *(dp + i) & (1 << 22)? 0:1 , *(dp + i) & (1 << 23)? 0:1, *(dp + i) & (1 << 24)? 0:1);
}
log_debug("\n");
    return 0;
}

int main(int argc, char **argv)
{
	setup(PULSE_WIDTH_INCREMENT_GRANULARITY_US_DEFAULT);
	int gpio[3]={22,23,24}, period[4]={50,10,5,2};
	int channel = 0,i=0;
	int subcycle_time_us = SUBCYCLE_TIME_US_DEFAULT;
	init_channel(channel, subcycle_time_us);
	print_channel(channel);

freq( channel, gpio[0], 50);	freq( channel, gpio[1], 5);	freq( channel, gpio[2], 10);
print_buf(channel);   sleep(30);

freq( channel, gpio[0], 1);	freq( channel, gpio[1], 20);	freq( channel, gpio[2], 2);
print_buf(channel);   sleep(30);

    shutdown();
    exit(0);
}



Please Log in or Create an account to join the conversation.

More
24 Apr 2013 23:13 #33150 by andypugh
I will try to get to this tonight, but getting my motorcycle working again is a slightly higher priority.

One thing that did occur to me is that if you have a conventional PC running LinuxCNC then you can halscope reading the p-port in the base thread as a DSO. You need the step length on the Pi to be longer than the base period of the PC, though.

Please Log in or Create an account to join the conversation.

More
25 Apr 2013 05:15 #33168 by mungkie

One thing that did occur to me is that if you have a conventional PC running LinuxCNC then you can halscope reading the p-port in the base thread as a DSO. You need the step length on the Pi to be longer than the base period of the PC, though.


Unfortunately I don't have a linuxcnc system that runs a base thread above 10khz I never really bothered trying to tune my linuxcnc setups as the stepper drives cannot run faster.

But I had a quick search and found an interesting link that may also be relevant to linuxcnc rpi gpio DMA drivers:

www.raspberrypi.org/phpBB3/viewtopic.php...b2d8a0cf16e&start=50

Seems the 'panalyzer' system runs on the rpi and can sample gpio at around ~20MHz, again using DMA but also I think some interupt masking maybe involved, and I think it looks like a system clock is used for better timing rather than the pwm (I am always unsure what other parts of the system maybe using clocks and interupts that may cause conflicts).

I am wondering if other parts of the linux kernel system maybe trying to use the DMA or causing interupts that effect the DMA, or maybe the scope is just faulty?

I am going to continue searching and wait on this for a while see what else turns up, there maybe easier or better ways to get gpio switched without effecting system performance.

Please Log in or Create an account to join the conversation.

More
25 Apr 2013 06:21 #33171 by andypugh
I am not seeing any glitches on my scope.
The following user(s) said Thank You: mungkie

Please Log in or Create an account to join the conversation.

Time to create page: 0.173 seconds
Powered by Kunena Forum