work work

This commit is contained in:
Olaf Rempel 2008-03-01 17:18:39 +01:00
parent e3ed2ef4e7
commit 588cfbc2c2
6 changed files with 227 additions and 783 deletions

173
main.c
View File

@ -1,95 +1,78 @@
/*************************************************************************** /***************************************************************************
* Copyright (C) 01/2008 by Olaf Rempel * * Copyright (C) 01/2008 by Olaf Rempel *
* razzor@kopf-tisch.de * * razzor@kopf-tisch.de *
* * * *
* This program is free software; you can redistribute it and/or modify * * 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 * * it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; version 2 of the License * * the Free Software Foundation; version 2 of the License *
* * * *
* This program is distributed in the hope that it will be useful, * * This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of * * but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. * * GNU General Public License for more details. *
* * * *
* You should have received a copy of the GNU General Public License * * You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the * * along with this program; if not, write to the *
* Free Software Foundation, Inc., * * Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/ ***************************************************************************/
#include "AT91SAM7S256.h" #include "AT91SAM7S256.h"
#include "at91_sysc.h" #include "at91_sysc.h"
#include "at91_dbgu.h" #include "at91_dbgu.h"
#include "at91_pitc.h" #include "at91_pitc.h"
#include "at91_tests.h" #include "at91_udp.h"
#include "at91_udp.h" #include "at91_pio.h"
#include "at91_pio.h" #include "at91_twi.h"
#include "at91_twi.h"
#include "memalloc.h"
#include "at91_tc1.h"
#include "memalloc.h" #include "board.h"
#include <stdio.h>
#include "board.h" #include <string.h>
#include <stdio.h>
#include <string.h> static uint32_t pitc_test(struct pitc_timer *timer)
{
static uint32_t pitc_test(struct pitc_timer *timer) static uint32_t i;
{ *AT91C_PIOA_SODR = i;
static uint32_t i; i = i ^ LED_GREEN;
*AT91C_PIOA_SODR = i; *AT91C_PIOA_CODR = i;
i = i ^ LED_GREEN;
*AT91C_PIOA_CODR = i; return PITC_RESTART_TIMER;
/* }
struct rc_values rc;
uint32_t count = rcontrol_getvalues(&rc); static struct pitc_timer pitc_test_timer = {
.interval = 10,
printf("%ld channels: ", count); .func = &pitc_test,
uint32_t j; };
for (j = 0; j < count; j++)
printf("%+5d ", rc.chan[j]); int main(void)
{
printf("\r"); /* LED outputs */
*/ *AT91C_PIOA_PER = LED_GREEN | LED_ORANGE;
return PITC_RESTART_TIMER; *AT91C_PIOA_OER = LED_GREEN | LED_ORANGE;
}
/* needed for dbgu */
static struct pitc_timer pitc_test_timer = { at91_sysc_init();
.interval = 10,
.func = &pitc_test, at91_dbgu_init();
}; at91_dbgu_puts("==========================================================\n\rGood morning Dave\n\r");
int main(void) /* triggers pinchange-isrs */
{ at91_pio_init();
/* LED outputs */
*AT91C_PIOA_PER = LED_GREEN | LED_ORANGE; /* timer */
*AT91C_PIOA_OER = LED_GREEN | LED_ORANGE; at91_pitc_init();
/* needed for dbgu */ /* twi */
at91_sysc_init(); at91_twi_init();
at91_twi_test();
at91_dbgu_init();
at91_dbgu_puts("==========================================================\n\rGood morning Dave\n\r"); /* usb */
at91_udp_init();
/* triggers pinchange-isrs */
at91_pio_init(); printf("static alloc: %5ld bytes\n\r", static_alloc_used());
/* timer */ pitc_schedule_timer(&pitc_test_timer);
at91_pitc_init();
at91_rttc_test_init(); while (1);
at91_tc1_init(); }
/* adc, need timer */
at91_adc_test_init();
/* twi */
at91_twi_init();
at91_twi_test();
/* usb */
at91_udp_init();
printf("static alloc: %5ld bytes\n\r", static_alloc_used());
pitc_schedule_timer(&pitc_test_timer);
while (1);
}

View File

@ -1,98 +0,0 @@
/***************************************************************************
* Copyright (C) 01/2008 by Olaf Rempel *
* razzor@kopf-tisch.de *
* *
* 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; version 2 of the License *
* *
* 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., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <stdint.h>
#include <stdio.h>
#include "AT91SAM7S256.h"
#include "board.h"
#include "at91_pitc.h"
static uint16_t adc_result[7];
static void at91_adc_isr(void)
{
AT91S_PDC *pdc = AT91C_BASE_PDC_ADC;
pdc->PDC_RPR = (uint32_t) &adc_result;
pdc->PDC_RCR = ARRAY_SIZE(adc_result);
pdc->PDC_PTCR = AT91C_PDC_RXTEN;
/* clear interrupts */
AT91S_ADC *adc = AT91C_BASE_ADC;
uint32_t status = adc->ADC_SR;
status = status;
}
static uint32_t adc_trigger(struct pitc_timer *timer)
{
uint32_t i;
for (i = 0; i < ARRAY_SIZE(adc_result); i++)
printf("0x%03x ", adc_result[i]);
printf("\n\r");
*AT91C_ADC_CR = AT91C_ADC_START;
return PITC_RESTART_TIMER;
}
static struct pitc_timer adc_timer = {
.interval = 10,
.func = &adc_trigger,
};
void at91_adc_test_init(void)
{
/* enable ADC clock */
*AT91C_PMC_PCER = (1 << AT91C_ID_ADC);
/* ADC Software reset */
AT91S_ADC *adc = AT91C_BASE_ADC;
adc->ADC_CR = AT91C_ADC_SWRST;
/*
* ADC config: 10bit, no sleep
* 4.8MHz (48MHz / ((4 +1) * 2) = 4.8MHz)
* 96 cycles Startup ((11 +1) * 8 / 4.8MHz = 20us)
* 3 cycles SH ((2 +1) / 4.8MHz = 625ns)
* Conversion time per channel @5MHz ~2us
*/
adc->ADC_MR = AT91C_ADC_TRGEN_DIS |
AT91C_ADC_LOWRES_10_BIT |
AT91C_ADC_SLEEP_NORMAL_MODE |
(AT91C_ADC_PRESCAL & (4 << 8)) |
(AT91C_ADC_STARTUP & (11 << 16)) |
(AT91C_ADC_SHTIM & (2 << 24));
/* setup PDC */
AT91S_PDC *pdc = AT91C_BASE_PDC_ADC;
pdc->PDC_RPR = (uint32_t) &adc_result;
pdc->PDC_RCR = ARRAY_SIZE(adc_result);
pdc->PDC_PTCR = AT91C_PDC_RXTEN;
/* enable 4 channels, PDC Interrupt */
adc->ADC_CHER = 0xF7;
adc->ADC_IER = AT91C_ADC_ENDRX;
/* low priority, level triggered, own vector */
AT91S_AIC *aic = AT91C_BASE_AIC;
aic->AIC_SMR[AT91C_ID_ADC] = IRQPRIO_ADC | AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL;
aic->AIC_SVR[AT91C_ID_ADC] = (uint32_t)at91_adc_isr;
aic->AIC_IECR = (1<<AT91C_ID_ADC);
// pitc_schedule_timer(&adc_timer);
}

View File

@ -1,33 +0,0 @@
#include <stdio.h>
#include "AT91SAM7S256.h"
#include "at91_sysc.h"
#include "board.h"
static void rtt_isr(uint32_t status)
{
*AT91C_RTTC_RTAR = *AT91C_RTTC_RTVR +1;
static uint32_t i;
*AT91C_PIOA_SODR = i;
i = i ^ LED_ORANGE;
*AT91C_PIOA_CODR = i;
}
void at91_rttc_test_init(void)
{
/* calculate SLOWCK from MAINCK and measured MAINF */
uint32_t prescaler = MAINCK * 16 / (*AT91C_CKGR_MCFR & AT91C_CKGR_MAINF);
sysc_register_isr(AT91_SYSIRQ_RTT, &rtt_isr);
/*
* AT91C_RTTC_RTTINCIEN doesn't work
* use AT91C_RTTC_ALMIEN and increment RTAR in isr
*/
*AT91C_RTTC_RTAR = *AT91C_RTTC_RTVR +1;
*AT91C_RTTC_RTMR = (AT91C_RTTC_RTPRES & prescaler) |
AT91C_RTTC_ALMIEN |
AT91C_RTTC_RTTRST;
printf("rttc running at %ld Hz\n\r", prescaler);
}

View File

@ -1,227 +0,0 @@
/***************************************************************************
* Copyright (C) 01/2008 by Olaf Rempel *
* razzor@kopf-tisch.de *
* *
* 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; version 2 of the License *
* *
* 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., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h> /* abs() */
#include "AT91SAM7S256.h"
#include "board.h"
#include "at91_tc1.h"
#include "at91_pio.h"
/* Hard limits for ISR */
#define PULSE_MIN 0x0500
#define PULSE_MAX 0x0D00
#define PULSE_TIMEOUT 0x0F00
#define PULSE_CENTER 0x08C0
/* moving average filters */
#define PULSE_FILTER_FAST (1<<2)
#define PULSE_FILTER_SLOW (1<<4)
#define PULSE_FILTER_DIFF 16 /* point to switch filters */
#define PULSE_MID_DIFF 50 /* minimum diff to center */
#define VALUE_RANGE 256
#define ROUND_DIV256(x) ((x >> 8) + ((x & 0x80) ? 1 : 0))
struct channel_data {
uint16_t width;
uint16_t width_slow;
uint16_t filter; /* 0 - fast filter, 1 - slow filter */
uint16_t min; /* minimum value during calibration */
uint16_t mid; /* center value */
uint16_t max; /* maximum value */
};
static struct channel_data ch_data[MAX_CHANNELS];
static uint32_t count, valid, cal_in_progress;
static void ppm_isr(void)
{
static uint32_t i;
/* RC Compare -> no TIOA1 edge for 2.5ms */
uint32_t status = *AT91C_TC1_SR;
if (status & AT91C_TC_CPCS) {
/* average channel count */
count = ((count * 7) + (i << 8)) / 8;
/* at least 4 channels and a stable channel count */
if ((ROUND_DIV256(count) == i) && (i >= 4)) {
if (valid < 10)
valid++;
} else if (valid > 0) {
valid--;
}
/* reset index */
i = 0;
}
/* edge on TIOA1 */
if (status & AT91C_TC_LDRAS) {
/* get impulse width */
uint16_t width = *AT91C_TC1_RA;
/* valid range: 1 - 2ms */
if (width > PULSE_MIN && width < PULSE_MAX) {
if (i < ARRAY_SIZE(ch_data)) {
/* calc both filters */
ch_data[i].width = ((ch_data[i].width * (PULSE_FILTER_FAST -1)) + width) / PULSE_FILTER_FAST;
ch_data[i].width_slow = ((ch_data[i].width_slow * (PULSE_FILTER_SLOW -1)) + width) / PULSE_FILTER_SLOW;
if (cal_in_progress) {
/* use slow filter values, calc center */
ch_data[i].min = MIN(ch_data[i].width_slow, ch_data[i].min);
ch_data[i].max = MAX(ch_data[i].width_slow, ch_data[i].max);
ch_data[i].mid = (ch_data[i].min + ch_data[i].max) / 2;
}
}
i++;
}
}
}
uint32_t rcontrol_getvalues(struct rc_values *rc)
{
if (valid < 5)
return 0;
uint32_t i;
uint32_t cnt = MIN(ROUND_DIV256(count), ARRAY_SIZE(ch_data));
for (i = 0; i < cnt; i++) {
/* switch between fast and slow filter */
uint16_t filter = (abs(ch_data[i].width - ch_data[i].width_slow) < PULSE_FILTER_DIFF);
/*
* transition fast -> slow filter
* slow filter is lagging behind, so give it a boost
*/
if (filter && !ch_data[i].filter && !cal_in_progress)
ch_data[i].width_slow = ch_data[i].width;
ch_data[i].filter = filter;
uint16_t width = (filter) ? ch_data[i].width_slow : ch_data[i].width;
/* expand the value to +/- VALUE_RANGE */
int32_t tmp = (width - ch_data[i].mid) * VALUE_RANGE;
tmp = tmp / ((tmp > 0) ? (ch_data[i].max - ch_data[i].mid) : (ch_data[i].mid - ch_data[i].min));
/* keep result in range */
if (tmp > VALUE_RANGE)
tmp = VALUE_RANGE;
if (tmp < -VALUE_RANGE)
tmp = -VALUE_RANGE;
rc->chan[i] = tmp;
}
return cnt;
}
void rcontrol_calibrate(uint32_t mode)
{
uint32_t i;
switch (mode) {
case RC_CAL_START:
cal_in_progress = 1;
for (i = 0; i < ARRAY_SIZE(ch_data); i++) {
/* use hard limits as hint */
ch_data[i].max = PULSE_MIN;
ch_data[i].mid = (PULSE_MIN + PULSE_MAX) / 2;
ch_data[i].min = PULSE_MAX;
}
break;
case RC_CAL_END:
cal_in_progress = 0;
for (i = 0; i < ARRAY_SIZE(ch_data); i++) {
/* treat current position as center */
ch_data[i].mid = ch_data[i].width_slow;
/* if center is near minimum, clamp output to 0..+1024 */
if (ch_data[i].mid - ch_data[i].min < PULSE_MID_DIFF)
ch_data[i].mid = ch_data[i].min;
/* if center is near maximum, clamp output to -1024..0 */
if (ch_data[i].max - ch_data[i].mid < PULSE_MID_DIFF)
ch_data[i].mid = ch_data[i].max;
}
break;
}
}
void rcontrol_print_cal(void)
{
uint32_t i;
for (i = 0; i < ARRAY_SIZE(ch_data); i++) {
printf("%ld: %d(%+d) - %d(0) - %d(%+d)\n\r", i,
ch_data[i].min, ch_data[i].min - ch_data[i].mid,
ch_data[i].mid,
ch_data[i].max, ch_data[i].max - ch_data[i].mid
);
}
}
void at91_tc1_init(void)
{
/* enable TC1 clock */
*AT91C_PMC_PCER = (1 << AT91C_ID_TC1);
/* MCK /32, trigger & capture on falling TIOA1 edge */
*AT91C_TC1_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK | AT91C_TC_LDRA_FALLING |
AT91C_TC_ETRGEDG_FALLING | AT91C_TC_ABETRG;
/* enable RA load and RC compare interrupt */
*AT91C_TC1_IER = AT91C_TC_LDRAS | AT91C_TC_CPCS;
/* RC Compare Interrupt if no rising Edge on TIOA1 for 2.56ms */
*AT91C_TC1_RC = PULSE_TIMEOUT;
/* enable & trigger the clock */
*AT91C_TC1_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
/* level triggered, own vector */
AT91S_AIC *aic = AT91C_BASE_AIC;
aic->AIC_SMR[AT91C_ID_TC1] = IRQPRIO_TC1 | AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL;
aic->AIC_SVR[AT91C_ID_TC1] = (uint32_t)ppm_isr;
aic->AIC_IECR = (1 << AT91C_ID_TC1);
}
static void tast_monitor(uint32_t status, uint32_t input)
{
if (!(input & TAST1) && (cal_in_progress == 0)) {
printf("start calibration\n\r");
rcontrol_calibrate(RC_CAL_START);
} else if (!(input & TAST2) && (cal_in_progress == 1)) {
printf("end calibration\n\r");
rcontrol_calibrate(RC_CAL_END);
rcontrol_print_cal();
}
}
PIO_PINCHANGE_ISR(TAST1 | TAST2, tast_monitor);

View File

@ -24,6 +24,7 @@
#include "usb_ch9.h" #include "usb_ch9.h"
#include "usb_cdc.h" #include "usb_cdc.h"
#include "usb_dfu.h"
#define csr_clear_flags(csr, flags) \ #define csr_clear_flags(csr, flags) \
while ((csr) & (flags)) \ while ((csr) & (flags)) \
@ -76,17 +77,76 @@ static const struct usb_device_descriptor dev_descriptor = {
.bNumConfigurations = 1, .bNumConfigurations = 1,
}; };
static const struct usb_string_descriptor usb_string0 = {
/* String 0 - Language */
.bLength = sizeof(struct usb_string_descriptor) + 1 * sizeof(uint16_t),
.bDescriptorType = USB_DT_STRING,
.wData = { 0x0409 /* English */ },
};
static const struct usb_string_descriptor usb_string1 = {
/* String 1 "sam7fc-flash" */
.bLength = sizeof(struct usb_string_descriptor) + 12 * sizeof(uint16_t),
.bDescriptorType = USB_DT_STRING,
.wData = {
0x0073, 0x0061, 0x006d, 0x0037, 0x0066, 0x0063, 0x002d, 0x0066,
0x006c, 0x0061, 0x0073, 0x0068,
},
};
static const struct usb_string_descriptor usb_string2 = {
/* String 2 "blctrl1-flash" */
.bLength = sizeof(struct usb_string_descriptor) + 13 * sizeof(uint16_t),
.bDescriptorType = USB_DT_STRING,
.wData = {
0x0062, 0x006c, 0x0063, 0x0074, 0x0072, 0x006c, 0x0031, 0x002d,
0x0066, 0x006c, 0x0061, 0x0073, 0x0068,
},
};
static const struct usb_string_descriptor usb_string3 = {
/* String 3 "blctrl2-flash" */
.bLength = sizeof(struct usb_string_descriptor) + 13 * sizeof(uint16_t),
.bDescriptorType = USB_DT_STRING,
.wData = {
0x0062, 0x006c, 0x0063, 0x0074, 0x0072, 0x006c, 0x0032, 0x002d,
0x0066, 0x006c, 0x0061, 0x0073, 0x0068,
},
};
static const struct usb_string_descriptor usb_string4 = {
/* String 4 "blctrl3-flash" */
.bLength = sizeof(struct usb_string_descriptor) + 13 * sizeof(uint16_t),
.bDescriptorType = USB_DT_STRING,
.wData = {
0x0062, 0x006c, 0x0063, 0x0074, 0x0072, 0x006c, 0x0033, 0x002d,
0x0066, 0x006c, 0x0061, 0x0073, 0x0068,
},
};
static const struct usb_string_descriptor usb_string5 = {
/* String 5 "blctrl4-flash" */
.bLength = sizeof(struct usb_string_descriptor) + 13 * sizeof(uint16_t),
.bDescriptorType = USB_DT_STRING,
.wData = {
0x0062, 0x006c, 0x0063, 0x0074, 0x0072, 0x006c, 0x0034, 0x002d,
0x0066, 0x006c, 0x0061, 0x0073, 0x0068,
},
};
static const struct usb_string_descriptor *usb_strings[] = {
&usb_string0, &usb_string1, &usb_string2, &usb_string3,
&usb_string4, &usb_string5,
};
struct my_config { struct my_config {
struct usb_config_descriptor cfg; struct usb_config_descriptor cfg;
struct usb_interface_descriptor ctrl_iface; struct usb_interface_descriptor iface0;
struct usb_cdc_header_desc cdc_header; struct usb_interface_descriptor iface1;
struct usb_cdc_call_mgmt_descriptor cdc_call_mgmt; struct usb_interface_descriptor iface2;
struct usb_cdc_acm_descriptor cdc_acm; struct usb_interface_descriptor iface3;
struct usb_cdc_union_desc cdc_union; struct usb_interface_descriptor iface4;
struct usb_endpoint_descriptor notify_ep; struct usb_dfu_descriptor dfu;
struct usb_interface_descriptor data_iface;
struct usb_endpoint_descriptor dataout_ep;
struct usb_endpoint_descriptor datain_ep;
} __attribute__ ((packed)); } __attribute__ ((packed));
static const struct my_config cfg_descriptor = { static const struct my_config cfg_descriptor = {
@ -94,82 +154,81 @@ static const struct my_config cfg_descriptor = {
.bLength = sizeof(struct usb_config_descriptor), .bLength = sizeof(struct usb_config_descriptor),
.bDescriptorType = USB_DT_CONFIG, .bDescriptorType = USB_DT_CONFIG,
.wTotalLength = sizeof(struct my_config), .wTotalLength = sizeof(struct my_config),
.bNumInterfaces = 2, .bNumInterfaces = 1,
.bConfigurationValue = 1, .bConfigurationValue = 1,
.bmAttributes = USB_CONFIG_ATT_SELFPOWER | USB_CONFIG_ATT_WAKEUP, .bmAttributes = USB_CONFIG_ATT_SELFPOWER | USB_CONFIG_ATT_WAKEUP,
.bMaxPower = 50, .bMaxPower = 50,
}, },
.ctrl_iface = { .iface0 = {
.bLength = sizeof(struct usb_interface_descriptor), .bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DT_INTERFACE, .bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0, .bInterfaceNumber = 0,
.bNumEndpoints = 1, .bInterfaceClass = USB_CLASS_APP_SPEC,
.bInterfaceClass = USB_CLASS_COMM, .bInterfaceSubClass = 0x01, /* DFU */
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, .bInterfaceProtocol = 0x02,
.bInterfaceProtocol = 1, .iInterface = 0x01,
}, },
.cdc_header = { .iface1 = {
.bLength = sizeof(struct usb_cdc_header_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
.bcdCDC = 0x0110,
},
.cdc_call_mgmt = {
.bLength = sizeof(struct usb_cdc_call_mgmt_descriptor),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
.bmCapabilities = USB_CDC_CALL_MGMT_CAP_CALL_MGMT,
.bDataInterface = 1,
},
.cdc_acm = {
.bLength = sizeof(struct usb_cdc_acm_descriptor),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_ACM_TYPE,
.bmCapabilities = (USB_CDC_CAP_BRK | USB_CDC_CAP_LINE | USB_CDC_COMM_FEATURE),
},
.cdc_union = {
.bLength = sizeof(struct usb_cdc_union_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_UNION_TYPE,
.bMasterInterface0 = 0,
.bSlaveInterface0 = 1,
},
.notify_ep = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN | 0x03,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = 64,
.bInterval = 10,
},
.data_iface = {
.bLength = sizeof(struct usb_interface_descriptor), .bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DT_INTERFACE, .bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 1, .bInterfaceNumber = 0,
.bNumEndpoints = 2, .bAlternateSetting = 1,
.bInterfaceClass = USB_CLASS_CDC_DATA, .bInterfaceClass = USB_CLASS_APP_SPEC,
.bInterfaceSubClass = 0x01, /* DFU */
.bInterfaceProtocol = 0x02,
.iInterface = 0x02,
}, },
.dataout_ep = { .iface2 = {
.bLength = sizeof(struct usb_endpoint_descriptor), .bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DT_ENDPOINT, .bDescriptorType = USB_DT_INTERFACE,
.bEndpointAddress = USB_DIR_OUT | 0x01, .bInterfaceNumber = 0,
.bmAttributes = USB_ENDPOINT_XFER_BULK, .bAlternateSetting = 2,
.wMaxPacketSize = 64, .bInterfaceClass = USB_CLASS_APP_SPEC,
.bInterfaceSubClass = 0x01, /* DFU */
.bInterfaceProtocol = 0x02,
.iInterface = 0x03,
}, },
.datain_ep = { .iface3 = {
.bLength = sizeof(struct usb_endpoint_descriptor), .bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DT_ENDPOINT, .bDescriptorType = USB_DT_INTERFACE,
.bEndpointAddress = USB_DIR_IN | 0x02, .bInterfaceNumber = 0,
.bmAttributes = USB_ENDPOINT_XFER_BULK, .bAlternateSetting = 3,
.wMaxPacketSize = 64, .bInterfaceClass = USB_CLASS_APP_SPEC,
.bInterfaceSubClass = 0x01, /* DFU */
.bInterfaceProtocol = 0x02,
.iInterface = 0x04,
}, },
.iface4 = {
.bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 4,
.bInterfaceClass = USB_CLASS_APP_SPEC,
.bInterfaceSubClass = 0x01, /* DFU */
.bInterfaceProtocol = 0x02,
.iInterface = 0x05,
},
.dfu = {
.bLength = sizeof(struct usb_dfu_descriptor),
.bDescriptorType = USB_TYPE_DFU,
.bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_CAN_UPLOAD,
.wDetachTimeOut = 0xff00,
.wTransferSize = AT91C_IFLASH_PAGE_SIZE,
.bcdDFUVersion = 0x0101,
},
};
static struct dfu_status dfu_status = {
.bStatus = DFU_STATUS_OK,
.bwPollTimeout = {0x00, 0x04, 0x00},
.bState = DFU_STATE_dfuIDLE,
}; };
static void ep_transfer_send(uint32_t ep, char *data, uint32_t length, static void ep_transfer_send(uint32_t ep, char *data, uint32_t length,
void (*complete_cb)(void)) void (*complete_cb)(void))
{ {
struct ep_ctx *ctx = &ep_ctx[ep]; struct ep_ctx *ctx = &ep_ctx[ep];
// printf("ep_transfer_send(%ld) size=%ld flags=0x%x\n\r", ep, length, ctx->flags); printf("ep_transfer_send(%ld) size=%ld flags=0x%x\n\r", ep, length, ctx->flags);
if (!(ctx->flags & CTX_TRANSFER) || (ctx->flags & (CTX_IN | CTX_OUT))) if (!(ctx->flags & CTX_TRANSFER) || (ctx->flags & (CTX_IN | CTX_OUT)))
return; return;
@ -196,7 +255,7 @@ static void ep_transfer_receive(uint32_t ep, char *data, uint32_t length,
void (*complete_cb)(void)) void (*complete_cb)(void))
{ {
struct ep_ctx *ctx = &ep_ctx[ep]; struct ep_ctx *ctx = &ep_ctx[ep];
// printf("ep_transfer_receive(%ld) size=%ld flags=0x%x\n\r", ep, length, ctx->flags); printf("ep_transfer_receive(%ld) size=%ld flags=0x%x\n\r", ep, length, ctx->flags);
if (!(ctx->flags & CTX_TRANSFER) || (ctx->flags & (CTX_IN | CTX_OUT))) if (!(ctx->flags & CTX_TRANSFER) || (ctx->flags & (CTX_IN | CTX_OUT)))
return; return;
@ -242,6 +301,7 @@ static void udp_configure_ep(const struct usb_endpoint_descriptor *desc)
*/ */
static void udp_txcb_setaddress(void) static void udp_txcb_setaddress(void)
{ {
printf("usb address: %d\n\r", current_address);
*AT91C_UDP_FADDR = (AT91C_UDP_FEN | current_address); *AT91C_UDP_FADDR = (AT91C_UDP_FEN | current_address);
*AT91C_UDP_GLBSTATE = AT91C_UDP_FADDEN; *AT91C_UDP_GLBSTATE = AT91C_UDP_FADDEN;
} }
@ -252,10 +312,7 @@ static void udp_txcb_setaddress(void)
*/ */
static void udp_txcb_setconfig(void) static void udp_txcb_setconfig(void)
{ {
udp_configure_ep(&cfg_descriptor.notify_ep); printf("usb configuration: %d\n\r", current_config);
udp_configure_ep(&cfg_descriptor.datain_ep);
udp_configure_ep(&cfg_descriptor.dataout_ep);
/* set UDP to "configured" */ /* set UDP to "configured" */
*AT91C_UDP_GLBSTATE = AT91C_UDP_CONFG; *AT91C_UDP_GLBSTATE = AT91C_UDP_CONFG;
} }
@ -292,6 +349,19 @@ static void ep_handle_ctrlrequest(struct usb_ctrlrequest *req)
NULL); NULL);
break; break;
case USB_DT_STRING: /* 0x03 */
;
uint8_t index = req->wValue & 0xFF;
if (index < ARRAY_SIZE(usb_strings)) {
printf("get string %d: %p\n\r", index, usb_strings[index]);
ep_transfer_send(0, (char *)usb_strings[index],
MIN(usb_strings[index]->bLength, req->wLength),
NULL);
} else {
ep_send_stall(0);
}
break;
default: default:
ep_send_stall(0); ep_send_stall(0);
break; break;
@ -326,12 +396,14 @@ static void ep_handle_ctrlrequest(struct usb_ctrlrequest *req)
case (USB_TYPE_CLASS | USB_RECIP_INTERFACE): /* 0x21/0xA1 */ case (USB_TYPE_CLASS | USB_RECIP_INTERFACE): /* 0x21/0xA1 */
// TODO: follow current_interface // TODO: follow current_interface
switch (req->bRequest) { switch (req->bRequest) {
case USB_CDC_REQ_SET_LINE_CODING: /* 0x20 */ case USB_REQ_DFU_DETACH: /* 0x00 */
/* read 7 bytes */ ep_transfer_send(0, NULL, 0, NULL);
break; break;
case USB_CDC_REQ_SET_CONTROL_LINE_STATE: /* 0x22 */ case USB_REQ_DFU_GETSTATUS: /* 0x03 */
ep_transfer_send(0, NULL, 0, NULL); ep_transfer_send(0, (char *)&dfu_status,
MIN(sizeof(struct dfu_status), req->wLength),
NULL);
break; break;
default: default:
@ -396,6 +468,8 @@ static void udp_handle_ep(uint32_t ep)
} else { } else {
ctx->flags &= ~CTX_IN; ctx->flags &= ~CTX_IN;
printf("txcomp\n\r");
if (transfer->complete_cb) if (transfer->complete_cb)
transfer->complete_cb(); transfer->complete_cb();
} }

View File

@ -1,255 +0,0 @@
/***************************************************************************
* Copyright (C) 02/2008 by Olaf Rempel *
* razzor@kopf-tisch.de *
* *
* 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; version 2 of the License *
* *
* 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., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "board.h" // ARRAY_SIZE()
#include "at91_pitc.h"
#include "telemetrie.h"
#include "memalloc.h"
#include "fifo.h"
/* extern symbols, defined in ldscript */
extern struct tdc_value _tdc_value_table;
extern struct tdc_value _tdc_value_table_end;
/* max. 8x 32 = 256 variables */
static uint32_t tdc_varmap[8];
/* array of devices, that are used to reach address X */
static struct comm_device *routing_table[8];
/*
* returns:
* -1: on routing error
* 0: no space left in txfifo (caller should retry)
* >0: success
*/
int32_t tdc_transmit(uint32_t addr, struct tdc_pkt_header *head)
{
if (addr >= ARRAY_SIZE(routing_table) || !routing_table[addr])
return -1;
return fifo_put(routing_table[addr]->txfifo, (char *)head, head->size);
}
static int32_t tdc_get_vars(void)
{
/* restart point */
static uint32_t id;
struct tdc_value *value = &_tdc_value_table + id;
while (value < &_tdc_value_table_end) {
uint32_t datalen = strlen(value->name);
struct tdc_getvars_reply *reply = alloc(sizeof(struct tdc_getvars_reply) + datalen);
reply->cmd = TDC_REPLY | TDC_ADDR1 | TDC_GETVARS;
reply->size = sizeof(struct tdc_getvars_reply) + datalen;
reply->id = id;
reply->flags = value->flags;
memcpy(reply->name, value->name, datalen);
uint32_t ret = tdc_transmit(TDC_ADDR0, ((struct tdc_pkt_header *)reply));
free(reply);
/* push routing error(-1) and retry(0) */
if (ret <= 0)
return ret;
id++;
value++;
}
/* dump complete, reset restart point */
id = 0;
return 1;
}
static int32_t tdc_get_value(uint32_t id)
{
struct tdc_value *value = &_tdc_value_table + id;
if (value >= &_tdc_value_table_end)
return -1;
uint32_t datalen = value->flags & TDC_SIZEMASK;
struct tdc_getvalue_reply *reply = alloc(sizeof(struct tdc_getvalue_reply) + datalen);
reply->cmd = TDC_REPLY | TDC_ADDR1 | TDC_GETVALUE;
reply->size = sizeof(struct tdc_getvars_reply) + datalen;
reply->id = id;
memcpy(reply->data, value->data, datalen);
int32_t ret = tdc_transmit(TDC_ADDR0, ((struct tdc_pkt_header *)reply));
free(reply);
return ret;
}
static int32_t tdc_set_value(uint32_t id, uint8_t *data)
{
struct tdc_value *value = &_tdc_value_table + id;
if (value >= &_tdc_value_table_end)
return -1;
uint32_t datalen = value->flags & TDC_SIZEMASK;
// TODO: atomic?
memcpy(value->data, data, datalen);
return 1;
}
static uint32_t tdc_timer_cb(struct pitc_timer *timer)
{
uint32_t i, j;
for (i = 0; i < ARRAY_SIZE(tdc_varmap); i++) {
uint32_t tmp = tdc_varmap[i];
for (j = 0; j < 32; j++) {
if (!tmp)
break;
if (tmp & 0x01) {
if (tdc_get_value(i * 32 + j) <= 0)
return PITC_REMOVE_TIMER;
}
tmp >>= 1;
}
}
return PITC_RESTART_TIMER;
}
static struct pitc_timer tdc_timer = {
.func = tdc_timer_cb,
};
static int32_t tdc_setup_timer(uint32_t interval, uint32_t *varmap)
{
memcpy(tdc_varmap, varmap, sizeof(tdc_varmap));
if (interval > 0) {
tdc_timer.interval = interval;
// TODO: timer already running
pitc_schedule_timer(&tdc_timer);
} else {
// TODO: timer stop
}
return 1;
}
static const struct tdc_hello_reply hello_reply = {
.cmd = TDC_REPLY | TDC_ADDR1 | TDC_HELLO,
.size = sizeof(struct tdc_hello_reply),
.name = "sam7fc-v0.01",
};
/*
* returns:
* -1: on routing error
* 0: no space left in txfifo (caller should retry)
* >0: success
*/
static int32_t tdc_parse_packet(struct tdc_pkt_header *head)
{
/* all replys go to the HOST */
if (head->cmd & TDC_REPLY)
return tdc_transmit(TDC_ADDR0, head);
/* forward this packet? */
if ((head->cmd & TDC_ADDRMASK) != TDC_ADDR1) {
uint32_t addr = (head->cmd & TDC_ADDRMASK) >> 4;
return tdc_transmit(addr, head);
}
int32_t ret = -1;
/* parse the packet */
switch (head->cmd & TDC_OPCODEMASK) {
case TDC_HELLO:
/* answer the hello */
ret = tdc_transmit(TDC_ADDR0, (struct tdc_pkt_header *)&hello_reply);
break;
case TDC_GETVARS:
ret = tdc_get_vars();
break;
case TDC_GETVALUE: {
struct tdc_getvalue_request *pkt = (struct tdc_getvalue_request *)head;
ret = tdc_get_value(pkt->id);
} break;
case TDC_SETVALUE: {
struct tdc_setvalue_request *pkt = (struct tdc_setvalue_request *)head;
ret = tdc_set_value(pkt->id, pkt->data);
} break;
case TDC_REQVALUES: {
struct tdc_reqvalues_request *pkt = (struct tdc_reqvalues_request *)head;
ret = tdc_setup_timer(pkt->interval, pkt->varmap);
} break;
};
/*
* on succes(>0) return size of request,
* and push retry(0) and routing error(-1) up
*/
return (ret > 0) ? head->size : ret;
}
void tdc_register_device(uint32_t addr, struct comm_device *device)
{
if (addr < ARRAY_SIZE(routing_table))
routing_table[addr] = device;
}
void tdc_receive(struct comm_device *device)
{
while (1) {
/* peek the header */
struct tdc_pkt_header tmp_head;
uint32_t len = fifo_peek(device->rxfifo, (char *)&tmp_head, sizeof(tmp_head));
if (len != sizeof(tmp_head))
return;
/* peek the whole packet */
struct tdc_pkt_header *head = alloc(tmp_head.size);
len = fifo_peek(device->rxfifo, (char *)head, tmp_head.size);
if (len != tmp_head.size)
return;
/* if it's a hello-request, remember the device as path to the host */
if ((head->cmd & (TDC_OPCODEMASK & TDC_DIR)) == TDC_HELLO)
tdc_register_device(TDC_ADDR0, device);
/* parse packet, remove data if no restart is needed */
int32_t ret = tdc_parse_packet(head);
free(head);
/* some tx-fifo was full(0), return to caller and let it retry */
if (!ret)
return;
/* success(>0) or routing error(-1) -> remove the packet */
fifo_remove(device->rxfifo, tmp_head.size);
}
}