Skip to content

Instantly share code, notes, and snippets.

@kinsamanka
Created November 8, 2014 06:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kinsamanka/94ba7940e4e222c844c8 to your computer and use it in GitHub Desktop.
Save kinsamanka/94ba7940e4e222c844c8 to your computer and use it in GitHub Desktop.
PWM
/* Copyright (C) 2013 GP Orcullo
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __HARDWARE_H__
#define __HARDWARE_H__
#define SYS_FREQ (80000000ul) /* 80 MHz */
#define GetSystemClock() (SYS_FREQ)
#define GetPeripheralClock() (GetSystemClock())
#define GetInstructionClock() (GetSystemClock())
#define SPICHAN 2
/* PORT USAGE
*
* Port Dir Signal
*
* RE0 OUT DIR_X
* RE1 OUT STEP_X
* RE2 OUT DIR_Y
* RE3 OUT STEP_Y
* RE4 OUT DIR_Z
* RE5 OUT STEP_Z
* RE6 OUT DIR_A
* RE7 OUT STEP_A
* RG8 OUT MISO
* RC13 OUT Status LED
* RC14 OUT DATA READY
*
* RD0 OUT PWM 0
* RD1 OUT PWM 1
* RD2 OUT PWM 2
*
* RD3 OUT OUTPUT 0
* RD4 OUT OUTPUT 1
* RD5 OUT OUTPUT 2
* RD6 OUT OUTPUT 3
* RD7 OUT OUTPUT 4
* RD8 OUT OUTPUT 5
* RD9 OUT OUTPUT 6
* RD10 OUT OUTPUT 7
* RD11 OUT OUTPUT 8
* RF0 OUT OUTPUT 9
* RF1 OUT OUTPUT 10
* RF3 OUT OUTPUT 11
*
* RG2 IN DATA REQUEST
* RG3 IN AUX
* RG6 IN SCLK
* RG7 IN MOSI
*
* RB0 IN ADC 0
* RB1 IN ADC 1
* RB2 IN ADC 2
*
* RB3 IN INPUT 0
* RB4 IN INPUT 1
* RB5 IN INPUT 2
* RB6 IN INPUT 3
* RB7 IN INPUT 4
* RB8 IN INPUT 5
* RB9 IN INPUT 6
* RB10 IN INPUT 7
* RB11 IN INPUT 8
* RB12 IN INPUT 9
* RB13 IN INPUT 10
* RB14 IN INPUT 11
* RB15 IN INPUT 12
*
*/
#define LED_TOGGLE (LATCINV = BIT_13)
#define REQ_IN (PORTGbits.RG2)
#define RDY_LO (LATCCLR = BIT_14)
#define RDY_HI (LATCSET = BIT_14)
#define PORTD_OUT_MASK (0xFF8)
#define PORTF_OUT_MASK (BIT_0 | BIT_1 | BIT_3)
#define STEP_X_LO (LATECLR = BIT_1)
#define STEP_X_HI (LATESET = BIT_1)
#define DIR_X_LO (LATECLR = BIT_0)
#define DIR_X_HI (LATESET = BIT_0)
#define STEP_Y_LO (LATECLR = BIT_3)
#define STEP_Y_HI (LATESET = BIT_3)
#define DIR_Y_LO (LATECLR = BIT_2)
#define DIR_Y_HI (LATESET = BIT_2)
#define STEP_Z_LO (LATECLR = BIT_5)
#define STEP_Z_HI (LATESET = BIT_5)
#define DIR_Z_LO (LATECLR = BIT_4)
#define DIR_Z_HI (LATESET = BIT_4)
#define STEP_A_LO (LATECLR = BIT_7)
#define STEP_A_HI (LATESET = BIT_7)
#define DIR_A_LO (LATECLR = BIT_6)
#define DIR_A_HI (LATESET = BIT_6)
#endif /* __HARDWARE_H__ */
/* Copyright (C) 2012 GP Orcullo
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <plib.h>
#include "hardware.h"
#include "stepgen.h"
#pragma config POSCMOD = XT /* Primary Oscillator XT mode */
#pragma config FNOSC = PRIPLL /* Primary Osc w/PLL */
#pragma config FPLLODIV = DIV_1 /* PLL configured for 80MHz clock */
#pragma config FPLLMUL = MUL_20
#pragma config FPLLIDIV = DIV_2
#pragma config FPBDIV = DIV_1 /* Peripheral Clock Divisor */
#pragma config IESO = ON /* Internal/External Switch Over disabled */
#pragma config FSOSCEN = OFF /* Secondary Oscillator disabled */
#pragma config CP = OFF /* Code Protect Disabled */
#pragma config FWDTEN = OFF /* Watchdog Timer Disable */
#pragma config WDTPS = PS4096 /* Watchdog Timer Postscaler */
#pragma config FVBUSONIO = OFF /* VBUSON pin is GPIO */
#pragma config FUSBIDIO = OFF /* USBID pin is GPIO */
#define BASEFREQ 160000
#define CORE_TICK_RATE (SYS_FREQ/2/BASEFREQ)
#define CORE_DIVIDER (BASEFREQ/CLOCK_CONF_SECOND)
#define SPIBUFSIZE 32
#define BUFSIZE (SPIBUFSIZE/4)
#define ENABLE_WATCHDOG
static volatile uint32_t rxBuf[BUFSIZE], txBuf[BUFSIZE];
static volatile int spi_data_ready;
static void init_io_ports()
{
U1PWRCbits.USUSPEND = 1;
U1PWRCbits.USBPWR = 0;
/* disable all analog pins except for pins 2-0 */
AD1PCFG = 0xFFF8;
/* configure inputs */
TRISBSET = 0xFFFF;
TRISGSET = BIT_2 | BIT_3 | BIT_6 | BIT_7;
/* configure_outputs */
TRISCCLR = BIT_13 | BIT_14;
TRISDCLR = 0xFFF;
TRISECLR = 0xFF;
TRISFCLR = BIT_0 | BIT_1 | BIT_3;
TRISGCLR = BIT_8;
/* enable open drain on step and dir outputs*/
ODCESET = BIT_7 | BIT_6 | BIT_5 | BIT_4 | BIT_3 | BIT_2 | BIT_1 | BIT_0;
/* enable open drain on all output pins */
ODCDSET = BIT_7 | BIT_6 | BIT_5 | BIT_4 | BIT_3 | BIT_2 | BIT_1 | BIT_0;
ODCDSET = BIT_11 | BIT_10 | BIT_9 | BIT_8;
ODCFSET = BIT_3 | BIT_1 | BIT_0;
/* data ready, active low */
RDY_HI;
}
static void init_spi()
{
int i;
SPI2CON = 0; /* stop SPI 2, set Slave mode, 8 bits, std buffer */
i = SPI2BUF; /* clear rcv buffer */
SPI2CON = 1<<8 | 0<<6; /* Clock Edge */
SPI2CONSET = 1<<15; /* start SPI 2 */
}
static void init_dma()
{
/* open and configure the DMA channels
DMA 0 is for SPI -> buffer, this is the master channel, auto enabled
DMA 1 is for buffer -> SPI, this channel is chained to DMA 0 */
DmaChnOpen(DMA_CHANNEL0, DMA_CHN_PRI3, DMA_OPEN_AUTO);
DmaChnOpen(DMA_CHANNEL1, DMA_CHN_PRI0, DMA_OPEN_DEFAULT);
/* DMA channels trigger on SPI RX, buffer not empty signal */
DmaChnSetEventControl(DMA_CHANNEL0, DMA_EV_START_IRQ(_SPI2_RX_IRQ));
DmaChnSetEventControl(DMA_CHANNEL1, DMA_EV_START_IRQ(_SPI2_TX_IRQ));
/* transfer 8bits at a time */
DmaChnSetTxfer(DMA_CHANNEL0, (void *)&SPI2BUF, (void *)rxBuf, 1, SPIBUFSIZE, 1);
DmaChnSetTxfer(DMA_CHANNEL1, (void *)txBuf, (void *)&SPI2BUF, SPIBUFSIZE, 1, 1);
/* start DMA 0 */
DmaChnEnable(0);
DmaChnEnable(1);
}
/* PWM is using OC1, OC2, OC3 and Timer2 */
static inline void configure_pwm()
{
OC1CON = 0x0000; /* disable OCx */
OC2CON = 0x0000;
OC3CON = 0x0000;
OC1R = 0; /* set output low */
OC2R = 0;
OC3R = 0;
OC1RS = 0;
OC2RS = 0;
OC3RS = 0;
OC1CON = 0x0006; /* PWM mode, fault pin disabled */
OC2CON = 0x0006;
OC3CON = 0x0006;
T2CONSET = 0x0008; /* Timer2 32 bit mode */
PR2 = 0x9C3F; /* set period, 1kHz */
T2CONSET = 0x8000; /* start timer */
OC1CONSET = 0x8020; /* enable OCx in 32 bit mode */
OC2CONSET = 0x8020;
OC3CONSET = 0x8020;
}
static inline void update_pwm_period(uint32_t val)
{
PR2 = val;
}
static inline void update_pwm_duty(uint32_t val1, uint32_t val2)
{
OC1RS = val1 >> 16;
OC2RS = val1 & 0xFFFF;
OC3RS = val2 >> 16;
}
static inline uint32_t read_inputs()
{
return (PORTB >> 3);
}
static inline void update_outputs(uint32_t val)
{
LATDCLR = PORTD_OUT_MASK & ~(val << 3);
LATDSET = PORTD_OUT_MASK & (val << 3);
val = val >> 9;
if (val && 0b100)
val = 0b1000 | (val && 0b11);
else
val = val && 0b11;
LATFCLR = PORTF_OUT_MASK & ~(val);
LATFSET = PORTF_OUT_MASK & (val);
}
void reset_board()
{
stepgen_reset();
update_outputs(0);
update_pwm_duty(0,0);
}
int main(void)
{
int spi_timeout, i;
unsigned long counter;
BMXCONbits.BMXARB = 0x02;
/* Disable JTAG port so we get our I/O pins back */
DDPCONbits.JTAGEN = 0;
/* Enable optimal performance */
SYSTEMConfigPerformance(GetSystemClock());
/* Use 1:1 CPU Core:Peripheral clocks */
OSCSetPBDIV(OSC_PB_DIV_1);
/* configure the core timer roll-over rate */
OpenCoreTimer(CORE_TICK_RATE);
/* set up the core timer interrupt */
mConfigIntCoreTimer((CT_INT_ON | CT_INT_PRIOR_6 | CT_INT_SUB_PRIOR_0));
/* enable multi vector interrupts */
INTConfigureSystem(INT_SYSTEM_CONFIG_MULT_VECTOR);
INTEnableInterrupts();
init_io_ports();
configure_pwm();
init_spi();
init_dma();
reset_board();
spi_data_ready = 0;
spi_timeout = 20000L;
counter = 0;
#if defined(ENABLE_WATCHDOG)
WDTCONSET = 0x8000;
#endif
/* main loop */
while (1) {
if (!REQ_IN) {
stepgen_get_position((void *)&txBuf[1]);
/* read inputs */
txBuf[1+MAXGEN] = read_inputs();
/* the ready line is active low */
RDY_LO;
} else {
RDY_HI;
}
if (spi_data_ready) {
spi_data_ready = 0;
/* reset spi_timeout */
spi_timeout = 20000L;
/* the first byte received is a command byte */
switch (rxBuf[0]) {
case 0x5453523E: /* >RST */
reset_board();
break;
case 0x444D433E: /* >CMD */
stepgen_update_input((const void *)&rxBuf[1]);
update_outputs(rxBuf[1+MAXGEN]);
update_pwm_duty(rxBuf[2+MAXGEN],rxBuf[3+MAXGEN]);
break;
case 0x4746433E: /* >CFG */
stepgen_update_stepwidth(rxBuf[1]);
update_pwm_period(rxBuf[2]);
stepgen_reset();
break;
case 0x5453543E: /* >TST */
for (i=0; i<BUFSIZE; i++)
txBuf[i] = rxBuf[i] ^ ~0;
break;
}
}
if (DCH0INTbits.CHBCIF) {
DCH0INTCLR = 1<<3;
/* data integrity check */
txBuf[0] = rxBuf[0] ^ ~0;
spi_data_ready = 1;
/* restart rx DMA */
DmaChnEnable(1);
}
/* shutdown stepgen if no activity */
if (spi_timeout)
spi_timeout--;
else
reset_board();
/* blink onboard led */
if (!(counter++ % (spi_timeout ? 0x10000 : 0x20000))) {
LED_TOGGLE;
}
#if defined(ENABLE_WATCHDOG)
/* keep alive */
WDTCONSET = 0x01;
#endif
}
return 0;
}
void __ISR(_CORE_TIMER_VECTOR, ipl6) CoreTimerHandler(void)
{
/* update the period */
UpdateCoreTimer(CORE_TICK_RATE);
/* do repetitive tasks here */
stepgen();
/* clear the interrupt flag */
mCTClearIntFlag();
}
/* Copyright (C) 2013 GP Orcullo
*
* Portions of this code is based on stepgen.c
* by John Kasunich, Copyright (C) 2003-2007
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "rtapi.h"
#include "rtapi_app.h"
#include "hal.h"
#include <math.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include "picnc.h"
#if !defined(BUILD_SYS_USER_DSO)
#error "This driver is for usermode threads only"
#endif
#if !defined(TARGET_PLATFORM_RASPBERRY)
#error "This driver is for the Raspberry Pi platform only"
#endif
#define MODNAME "picnc"
#define PREFIX "picnc"
MODULE_AUTHOR("GP Orcullo");
MODULE_DESCRIPTION("Driver for Raspberry Pi PICnc board");
MODULE_LICENSE("GPL v2");
static int stepwidth = 1;
RTAPI_MP_INT(stepwidth, "Step width in 1/BASEFREQ");
static long pwmfreq = 500;
RTAPI_MP_LONG(pwmfreq, "PWM frequency in Hz");
typedef struct {
hal_float_t *position_cmd[NUMAXES],
*position_fb[NUMAXES],
*pwm_duty[3],
*adc_in[3];
hal_bit_t *out[12], *inp[13],
*inp_inv[13],
*ready, *fault;
hal_float_t scale[NUMAXES],
maxaccel[NUMAXES],
adc_scale[3],
pwm_scale[3];
hal_u32_t *test;
} data_t;
static data_t *data;
static int comp_id;
static const char *modname = MODNAME;
static const char *prefix = PREFIX;
volatile unsigned *gpio, *spi;
volatile int32_t txBuf[BUFSIZE], rxBuf[BUFSIZE];
static u32 pwm_period = 0;
static double dt = 0, /* update_freq period in seconds */
recip_dt = 0, /* reciprocal of period, avoids divides */
scale_inv[NUMAXES] = { 1.0 }, /* inverse of scale */
old_vel[NUMAXES] = { 0 },
old_pos[NUMAXES] = { 0 },
old_scale[NUMAXES] = { 0 },
max_vel;
static long old_dtns = 0; /* update_freq funct period in nsec */
static s32 accum_diff = 0,
old_count[NUMAXES] = { 0 };
static s64 accum[NUMAXES] = { 0 }; /* 64 bit DDS accumulator */
static void read_spi(void *arg, long period);
static void write_spi(void *arg, long period);
static void update(void *arg, long period);
void transfer_data();
static void reset_board();
static int map_gpio();
static void setup_gpio();
static void restore_gpio();
int rtapi_app_main(void)
{
char name[HAL_NAME_LEN + 1];
int n, retval;
/* initialise driver */
comp_id = hal_init(modname);
if (comp_id < 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "%s: ERROR: hal_init() failed\n",
modname);
return -1;
}
/* allocate shared memory */
data = hal_malloc(sizeof(data_t));
if (data == 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "%s: ERROR: hal_malloc() failed\n",
modname);
hal_exit(comp_id);
return -1;
}
/* configure board */
retval = map_gpio();
if (retval < 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"%s: ERROR: cannot map GPIO memory\n", modname);
return retval;
}
setup_gpio();
reset_board();
pwm_period = (SYS_FREQ/pwmfreq) - 1; /* PeripheralClock/pwmfreq - 1 */
txBuf[0] = 0x4746433E; /* this is config data (>CFG) */
txBuf[1] = stepwidth;
txBuf[2] = pwm_period;
transfer_data(); /* send config data */
max_vel = BASEFREQ/(4.0 * stepwidth); /* calculate velocity limit */
/* export pins and parameters */
for (n=0; n<NUMAXES; n++) {
retval = hal_pin_float_newf(HAL_IN, &(data->position_cmd[n]),
comp_id, "%s.axis.%01d.position-cmd", prefix, n);
if (retval < 0) goto error;
*(data->position_cmd[n]) = 0.0;
retval = hal_pin_float_newf(HAL_OUT, &(data->position_fb[n]),
comp_id, "%s.axis.%01d.position-fb", prefix, n);
if (retval < 0) goto error;
*(data->position_fb[n]) = 0.0;
retval = hal_param_float_newf(HAL_RW, &(data->scale[n]),
comp_id, "%s.axis.%01d.scale", prefix, n);
if (retval < 0) goto error;
data->scale[n] = 1.0;
retval = hal_param_float_newf(HAL_RW, &(data->maxaccel[n]),
comp_id, "%s.axis.%01d.maxaccel", prefix, n);
if (retval < 0) goto error;
data->maxaccel[n] = 1.0;
}
for (n=0; n<3; n++) {
retval = hal_pin_float_newf(HAL_IN, &(data->pwm_duty[n]),
comp_id, "%s.pwm.%01d.duty", prefix, n);
if (retval < 0) goto error;
*(data->pwm_duty[n]) = 0.0;
retval = hal_param_float_newf(HAL_RW, &(data->pwm_scale[n]),
comp_id, "%s.pwm.%01d.scale", prefix, n);
if (retval < 0) goto error;
data->pwm_scale[n] = 1.0;
retval = hal_pin_float_newf(HAL_OUT, &(data->adc_in[n]),
comp_id, "%s.adc.%01d.val", prefix, n);
if (retval < 0) goto error;
*(data->adc_in[n]) = 0.0;
retval = hal_param_float_newf(HAL_RW, &(data->adc_scale[n]),
comp_id, "%s.adc.%01d.scale", prefix, n);
if (retval < 0) goto error;
data->adc_scale[n] = 1.0;
}
for (n=0; n<13; n++) {
retval = hal_pin_bit_newf(HAL_OUT, &(data->inp[n]), comp_id,
"%s.input.%01d.pin", prefix, n);
if (retval < 0) goto error;
*(data->inp[n]) = 0;
retval = hal_pin_bit_newf(HAL_OUT, &(data->inp_inv[n]), comp_id,
"%s.input.%01d.pin_inv", prefix, n);
if (retval < 0) goto error;
*(data->inp_inv[n]) = 1;
}
for (n=0; n<12; n++) {
retval = hal_pin_bit_newf(HAL_IN, &(data->out[n]), comp_id,
"%s.output.%01d.pin", prefix, n);
if (retval < 0) goto error;
*(data->out[n]) = 0;
}
retval = hal_pin_bit_newf(HAL_OUT, &(data->ready), comp_id,
"%s.ready", prefix);
if (retval < 0) goto error;
*(data->ready) = 0;
retval = hal_pin_bit_newf(HAL_IO, &(data->fault), comp_id,
"%s.fault", prefix);
if (retval < 0) goto error;
*(data->fault) = 0;
retval = hal_pin_u32_newf(HAL_IN, &(data->test), comp_id,
"%s.test", prefix);
if (retval < 0) goto error;
*(data->test) = 0;
error:
if (retval < 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"%s: ERROR: pin export failed with err=%i\n",
modname, retval);
hal_exit(comp_id);
return -1;
}
/* export functions */
rtapi_snprintf(name, sizeof(name), "%s.read", prefix);
retval = hal_export_funct(name, read_spi, data, 1, 0, comp_id);
if (retval < 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"%s: ERROR: read function export failed\n", modname);
hal_exit(comp_id);
return -1;
}
rtapi_snprintf(name, sizeof(name), "%s.write", prefix);
/* no FP operations */
retval = hal_export_funct(name, write_spi, data, 0, 0, comp_id);
if (retval < 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"%s: ERROR: write function export failed\n", modname);
hal_exit(comp_id);
return -1;
}
rtapi_snprintf(name, sizeof(name), "%s.update", prefix);
retval = hal_export_funct(name, update, data, 1, 0, comp_id);
if (retval < 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"%s: ERROR: update function export failed\n", modname);
hal_exit(comp_id);
return -1;
}
rtapi_print_msg(RTAPI_MSG_INFO, "%s: installed driver\n", modname);
hal_ready(comp_id);
return 0;
}
void rtapi_app_exit(void)
{
restore_gpio();
munmap((void *)gpio,BLOCK_SIZE);
munmap((void *)spi,BLOCK_SIZE);
hal_exit(comp_id);
}
static inline void update_inputs(data_t *dat)
{
int n;
for (n=0; n<13; n++) {
*(dat->inp[n]) = (get_inputs() & (1l << n)) ? 1 : 0;
*(dat->inp_inv[n]) = ~(*(dat->inp[n]));
}
*(dat->adc_in[0]) = dat->adc_scale[0] * ((u32)get_adc(0) >> 16);
*(dat->adc_in[1]) = dat->adc_scale[1] * (get_adc(0) & 0xFFFF);
*(dat->adc_in[2]) = dat->adc_scale[2] * ((u32)get_adc(1) >> 16);
}
static void read_spi(void *arg, long period)
{
int i;
static int startup = 0;
data_t *dat = (data_t *)arg;
unsigned long timeout = REQ_TIMEOUT;
/* skip loading velocity command */
txBuf[0] = 0x444D4300;
/* send request */
BCM2835_GPCLR0 = (1l << 23);
/* wait until ready, signal active low */
while ((BCM2835_GPLEV0 & (1l << 25)) && (timeout--));
*(dat->test) = timeout;
/* clear request, active low */
BCM2835_GPSET0 = (1l << 23);
if (timeout) transfer_data();
/* sanity check */
if (rxBuf[0] == (0x444D433E ^ ~0)) {
*(dat->ready) = 1;
} else {
*(dat->ready) = 0;
if (!startup)
startup = 1;
else
*(dat->fault) = 1;
}
/* check for change in period */
if (period != old_dtns) {
old_dtns = period;
dt = period * 0.000000001;
recip_dt = 1.0 / dt;
}
/* check for scale change */
for (i = 0; i < NUMAXES; i++) {
if (dat->scale[i] != old_scale[i]) {
old_scale[i] = dat->scale[i];
/* scale must not be 0 */
if ((dat->scale[i] < 1e-20) && (dat->scale[i] > -1e-20))
dat->scale[i] = 1.0;
scale_inv[i] = (1.0 / STEP_MASK) / dat->scale[i];
}
}
/* update outputs */
for (i = 0; i < NUMAXES; i++) {
/* the DDS uses 32 bit counter, this code converts
that counter into 64 bits */
accum_diff = get_position(i) - old_count[i];
old_count[i] = get_position(i);
accum[i] += accum_diff;
*(dat->position_fb[i]) = (float)(accum[i]) * scale_inv[i];
}
/* update input status */
update_inputs(dat);
}
static void write_spi(void *arg, long period)
{
transfer_data();
}
static inline void update_outputs(data_t *dat)
{
float duty;
int n;
u32 x[3];
s32 y;
/* update pic32 output */
for (n = 0, y = 0; n < 12; n++)
y |= (*(dat->out[n]) ? 1l : 0) << n;
txBuf[1 + NUMAXES] = y;
/* update pwm */
for (n = 0; n < 3; n++) {
duty = *(dat->pwm_duty[n]) * dat->pwm_scale[n] * 0.01;
if (duty < 0.0) duty = 0.0;
if (duty > 1.0) duty = 1.0;
x[n] = (duty * (1.0 + pwm_period));
}
txBuf[2+NUMAXES] = x[0] << 16 | x[1];
txBuf[3+NUMAXES] = x[2] << 16;
}
static void update(void *arg, long period)
{
int i;
data_t *dat = (data_t *)arg;
double max_accl, vel_cmd, dv, new_vel,
dp, pos_cmd, curr_pos, match_accl, match_time, avg_v,
est_out, est_cmd, est_err;
for (i = 0; i < NUMAXES; i++) {
/* set internal accel limit to its absolute max, which is
zero to full speed in one thread period */
max_accl = max_vel * recip_dt;
/* check for user specified accel limit parameter */
if (dat->maxaccel[i] <= 0.0) {
/* set to zero if negative */
dat->maxaccel[i] = 0.0;
} else {
/* parameter is non-zero, compare to max_accl */
if ((dat->maxaccel[i] * fabs(dat->scale[i])) > max_accl) {
/* parameter is too high, lower it */
dat->maxaccel[i] = max_accl / fabs(dat->scale[i]);
} else {
/* lower limit to match parameter */
max_accl = dat->maxaccel[i] * fabs(dat->scale[i]);
}
}
/* calculate position command in counts */
pos_cmd = *(dat->position_cmd[i]) * dat->scale[i];
/* calculate velocity command in counts/sec */
vel_cmd = (pos_cmd - old_pos[i]) * recip_dt;
old_pos[i] = pos_cmd;
/* apply frequency limit */
if (vel_cmd > max_vel) {
vel_cmd = max_vel;
} else if (vel_cmd < -max_vel) {
vel_cmd = -max_vel;
}
/* determine which way we need to ramp to match velocity */
if (vel_cmd > old_vel[i])
match_accl = max_accl;
else
match_accl = -max_accl;
/* determine how long the match would take */
match_time = (vel_cmd - old_vel[i]) / match_accl;
/* calc output position at the end of the match */
avg_v = (vel_cmd + old_vel[i]) * 0.5;
curr_pos = (double)(accum[i]) * (1.0 / STEP_MASK);
est_out = curr_pos + avg_v * match_time;
/* calculate the expected command position at that time */
est_cmd = pos_cmd + vel_cmd * (match_time - 1.5 * dt);
/* calculate error at that time */
est_err = est_out - est_cmd;
if (match_time < dt) {
/* we can match velocity in one period */
if (fabs(est_err) < 0.0001) {
/* after match the position error will be acceptable */
/* so we just do the velocity match */
new_vel = vel_cmd;
} else {
/* try to correct position error */
new_vel = vel_cmd - 0.5 * est_err * recip_dt;
/* apply accel limits */
if (new_vel > (old_vel[i] + max_accl * dt)) {
new_vel = old_vel[i] + max_accl * dt;
} else if (new_vel < (old_vel[i] - max_accl * dt)) {
new_vel = old_vel[i] - max_accl * dt;
}
}
} else {
/* calculate change in final position if we ramp in the
opposite direction for one period */
dv = -2.0 * match_accl * dt;
dp = dv * match_time;
/* decide which way to ramp */
if (fabs(est_err + dp * 2.0) < fabs(est_err)) {
match_accl = -match_accl;
}
/* and do it */
new_vel = old_vel[i] + match_accl * dt;
}
/* apply frequency limit */
if (new_vel > max_vel) {
new_vel = max_vel;
} else if (new_vel < -max_vel) {
new_vel = -max_vel;
}
old_vel[i] = new_vel;
/* calculate new velocity cmd */
update_velocity(i, (new_vel * VELSCALE));
}
update_outputs(dat);
/* this is a command (>CMD) */
txBuf[0] = 0x444D433E;
}
void transfer_data()
{
char *buf;
int i;
/* activate transfer */
BCM2835_SPICS = SPI_CS_TA;
/* send txBuf */
buf = (char *)txBuf;
for (i=0; i<SPIBUFSIZE; i++) {
BCM2835_SPIFIFO = *buf++;
}
/* wait until transfer is finished */
while (!(BCM2835_SPICS & SPI_CS_DONE));
/* clear DONE bit */
BCM2835_SPICS = SPI_CS_DONE;
/* read buffer */
buf = (char *)rxBuf;
for (i=0; i<SPIBUFSIZE; i++) {
*buf++ = BCM2835_SPIFIFO;
}
}
int map_gpio()
{
int fd;
fd = open("/dev/mem", O_RDWR | O_SYNC);
if (fd < 0) {
rtapi_print_msg(RTAPI_MSG_ERR,"%s: can't open /dev/mem \n",modname);
return -1;
}
/* mmap GPIO */
gpio = mmap(
NULL,
BLOCK_SIZE,
PROT_READ|PROT_WRITE,
MAP_SHARED,
fd,
BCM2835_GPIO_BASE);
if (gpio == MAP_FAILED) {
rtapi_print_msg(RTAPI_MSG_ERR,"%s: can't map gpio\n",modname);
close(fd);
return -1;
}
/* mmap SPI */
spi = mmap(
NULL,
BLOCK_SIZE,
PROT_READ|PROT_WRITE,
MAP_SHARED,
fd,
BCM2835_SPI_BASE);
close(fd);
if (spi == MAP_FAILED) {
rtapi_print_msg(RTAPI_MSG_ERR,"%s: can't map spi\n",modname);
return -1;
}
return 0;
}
/* GPIO USAGE
*
* GPIO Dir Signal Note
*
* 25 IN DATA READY active low
* 9 IN MISO SPI
* 23 OUT DATA REQUEST active low
* 10 OUT MOSI SPI
* 11 OUT SCLK SPI
* 7 OUT RESET active low
*
*/
void setup_gpio()
{
u32 x;
/* data ready GPIO 25, input */
x = BCM2835_GPFSEL2;
x &= ~(0b111 << (5*3));
BCM2835_GPFSEL2 = x;
/* data request GPIO 23, output */
x = BCM2835_GPFSEL2;
x &= ~(0b111 << (3*3));
x |= (0b001 << (3*3));
BCM2835_GPFSEL2 = x;
/* reset GPIO 7, output */
x = BCM2835_GPFSEL0;
x &= ~(0b111 << (7*3));
x |= (0b001 << (7*3));
BCM2835_GPFSEL0 = x;
/* change SPI pins */
x = BCM2835_GPFSEL0;
x &= ~(0b111 << (9*3));
x |= (0b100 << (9*3));
BCM2835_GPFSEL0 = x;
x = BCM2835_GPFSEL1;
x &= ~(0b111 << (0*3) | 0b111 << (1*3));
x |= (0b100 << (0*3) | 0b100 << (1*3));
BCM2835_GPFSEL1 = x;
/* set up SPI */
BCM2835_SPICLK = SPICLKDIV;
BCM2835_SPICS = 0;
/* clear FIFOs */
BCM2835_SPICS |= SPI_CS_CLEAR_RX | SPI_CS_CLEAR_TX;
/* clear done bit */
BCM2835_SPICS |= SPI_CS_DONE;
}
void restore_gpio()
{
u32 x;
/* change all used pins back to inputs */
/* GPIO 7 */
x = BCM2835_GPFSEL0;
x &= ~(0b111 << (7*3));
BCM2835_GPFSEL0 = x;
/* GPIO 23 */
x = BCM2835_GPFSEL2;
x &= ~(0b111 << (3*3));
BCM2835_GPFSEL2 = x;
/* GPIO 25 */
x = BCM2835_GPFSEL2;
x &= ~(0b111 << (5*3));
BCM2835_GPFSEL2 = x;
/* change SPI pins to inputs*/
x = BCM2835_GPFSEL0;
x &= ~(0b111 << (9*3));
BCM2835_GPFSEL0 = x;
x = BCM2835_GPFSEL1;
x &= ~(0b111 << (0*3) | 0b111 << (1*3));
BCM2835_GPFSEL1 = x;
}
void reset_board()
{
u32 x,i;
/* GPIO 7 is configured as a tri-state output pin */
/* set as output GPIO 7 */
x = BCM2835_GPFSEL0;
x &= ~(0b111 << (7*3));
x |= (0b001 << (7*3));
BCM2835_GPFSEL0 = x;
/* board reset is active low */
for (i=0; i<0x10000; i++)
BCM2835_GPCLR0 = (1l << 7);
/* wait until the board is ready */
for (i=0; i<0x300000; i++)
BCM2835_GPSET0 = (1l << 7);
/* reset GPIO 7 back to input */
x = BCM2835_GPFSEL0;
x &= ~(0b111 << (7*3));
BCM2835_GPFSEL0 = x;
}
/* Copyright (C) 2013 GP Orcullo
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef PICNC_H
#define PICNC_H
#define SPICLKDIV 16 /* ~15 Mhz */
#define NUMAXES 4 /* X Y Z A */
#define REQ_TIMEOUT 10000ul
#define SPIBUFSIZE 32 /* SPI buffer size */
#define BUFSIZE (SPIBUFSIZE/4)
#define STEPBIT 23 /* bit location in DDS accum */
#define STEP_MASK (1<<STEPBIT)
#define BASEFREQ 160000ul /* Base freq of the PIC stepgen in Hz */
#define SYS_FREQ (80000000ul) /* 80 MHz */
#define PERIODFP ((double)1.0 / (double)(BASEFREQ))
#define VELSCALE ((double)STEP_MASK * PERIODFP)
#define ACCELSCALE (VELSCALE * PERIODFP)
#define get_position(a) (rxBuf[1 + (a)])
#define get_inputs() (rxBuf[1 + NUMAXES])
#define set_outputs (txBuf[1 + NUMAXES])
#define get_adc(a) (rxBuf[2 + NUMAXES + a])
#define update_velocity(a, b) (txBuf[1 + (a)] = (b))
/* Broadcom defines */
#define BCM2835_PERI_BASE 0x20000000
#define BCM2835_GPIO_BASE (BCM2835_PERI_BASE + 0x200000) /* GPIO controller */
#define BCM2835_SPI_BASE (BCM2835_PERI_BASE + 0x204000) /* SPI controller */
#define BCM2835_GPFSEL0 *(gpio)
#define BCM2835_GPFSEL1 *(gpio + 1)
#define BCM2835_GPFSEL2 *(gpio + 2)
#define BCM2835_GPFSEL3 *(gpio + 3)
#define BCM2835_GPFSEL4 *(gpio + 4)
#define BCM2835_GPFSEL5 *(gpio + 5)
#define BCM2835_GPSET0 *(gpio + 7)
#define BCM2835_GPSET1 *(gpio + 8)
#define BCM2835_GPCLR0 *(gpio + 10)
#define BCM2835_GPCLR1 *(gpio + 11)
#define BCM2835_GPLEV0 *(gpio + 13)
#define BCM2835_GPLEV1 *(gpio + 14)
#define BCM2835_SPICS *(spi + 0)
#define BCM2835_SPIFIFO *(spi + 1)
#define BCM2835_SPICLK *(spi + 2)
#define SPI_CS_LEN_LONG 0x02000000
#define SPI_CS_DMA_LEN 0x01000000
#define SPI_CS_CSPOL2 0x00800000
#define SPI_CS_CSPOL1 0x00400000
#define SPI_CS_CSPOL0 0x00200000
#define SPI_CS_RXF 0x00100000
#define SPI_CS_RXR 0x00080000
#define SPI_CS_TXD 0x00040000
#define SPI_CS_RXD 0x00020000
#define SPI_CS_DONE 0x00010000
#define SPI_CS_LEN 0x00002000
#define SPI_CS_REN 0x00001000
#define SPI_CS_ADCS 0x00000800
#define SPI_CS_INTR 0x00000400
#define SPI_CS_INTD 0x00000200
#define SPI_CS_DMAEN 0x00000100
#define SPI_CS_TA 0x00000080
#define SPI_CS_CSPOL 0x00000040
#define SPI_CS_CLEAR_RX 0x00000020
#define SPI_CS_CLEAR_TX 0x00000010
#define SPI_CS_CPOL 0x00000008
#define SPI_CS_CPHA 0x00000004
#define SPI_CS_CS_10 0x00000002
#define SPI_CS_CS_01 0x00000001
#define PAGE_SIZE (4*1024)
#define BLOCK_SIZE (4*1024)
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment