working init-ctx version
This commit is contained in:
parent
0302d9bdb0
commit
011d784923
27
include/rtos/context.h
Normal file
27
include/rtos/context.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef _CONTEXT_H_
|
||||
#define _CONTEXT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "rtos/spinlock.h"
|
||||
|
||||
enum state_t {
|
||||
CONTEXT_NULL = 0x00,
|
||||
CONTEXT_RUNNING,
|
||||
CONTEXT_READY,
|
||||
CONTEXT_INTERRUPTED,
|
||||
CONTEXT_SLEEP,
|
||||
CONTEXT_SLEEP_QUEUE,
|
||||
};
|
||||
|
||||
struct context {
|
||||
state_t state;
|
||||
uint8_t priority;
|
||||
uint32_t spinlock_held;
|
||||
uint32_t dummy;
|
||||
};
|
||||
|
||||
uint32_t context_wait_priority_queue(struct spinlock *lock, struct context **queue);
|
||||
uint32_t context_signal_queue(struct context **queue);
|
||||
uint32_t context_interrupt_queue(struct context *c, struct context **queue);
|
||||
|
||||
#endif /* _CONTEXT_H_ */
|
10
include/rtos/critical.h
Normal file
10
include/rtos/critical.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef _CRITICAL_H_
|
||||
#define _CRITICAL_H_
|
||||
|
||||
/* disable Interrupts */
|
||||
void enter_critical_section(void);
|
||||
|
||||
/* enable Interrupts */
|
||||
void exit_critical_section(void);
|
||||
|
||||
#endif /* _CRITICAL_H_ */
|
22
include/rtos/semaphore.h
Normal file
22
include/rtos/semaphore.h
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef _SEMSPHORE_H_
|
||||
#define _SEMSPHORE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "rtos/context.h"
|
||||
#include "rtos/spinlock.h"
|
||||
|
||||
struct semaphore {
|
||||
struct context *sleep_queue;
|
||||
struct spinlock lock;
|
||||
uint32_t count;
|
||||
};
|
||||
|
||||
state_t sem_wait(struct semaphore *sem);
|
||||
void sem_post(struct semaphore *sem);
|
||||
|
||||
void sem_interrupt(struct semaphore *sem, struct context *c);
|
||||
int32_t sem_get_count(struct semaphore *sem);
|
||||
void sem_init(struct semaphore *sem, uint8_t priority, int32_t count);
|
||||
|
||||
#endif /* _SEMSPHORE_H_ */
|
16
include/rtos/spinlock.h
Normal file
16
include/rtos/spinlock.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef _SPINLOCK_H_
|
||||
#define _SPINLOCK_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct spinlock {
|
||||
uint8_t locked;
|
||||
uint8_t priority_locked;
|
||||
uint8_t priority_unlocked;
|
||||
};
|
||||
|
||||
void spinlock_lock(struct spinlock *lock);
|
||||
void spinlock_unlock(struct spinlock *lock);
|
||||
void spinlock_init(struct spinlock *lock, uint8_t priority);
|
||||
|
||||
#endif /* _SPINLOCK_H_ */
|
118
main.c
118
main.c
@ -16,46 +16,76 @@
|
||||
* 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_sysc.h"
|
||||
#include "at91_dbgu.h"
|
||||
#include "at91_pitc.h"
|
||||
#include "at91_tests.h"
|
||||
#include "at91_udp.h"
|
||||
#include "at91_pio.h"
|
||||
#include "at91_twi.h"
|
||||
|
||||
#include "at91_tc1.h"
|
||||
#include "memalloc.h"
|
||||
|
||||
#include "board.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static uint32_t pitc_test(struct pitc_timer *timer)
|
||||
uint32_t * init_ctx(uint32_t *stack, void *code, void *arg)
|
||||
{
|
||||
static uint32_t i;
|
||||
*AT91C_PIOA_SODR = i;
|
||||
i = i ^ LED_GREEN;
|
||||
*AT91C_PIOA_CODR = i;
|
||||
/*
|
||||
struct rc_values rc;
|
||||
uint32_t count = rcontrol_getvalues(&rc);
|
||||
uint32_t org_stack = (uint32_t)stack;
|
||||
|
||||
printf("%ld channels: ", count);
|
||||
uint32_t j;
|
||||
for (j = 0; j < count; j++)
|
||||
printf("%+5d ", rc.chan[j]);
|
||||
*stack-- = (uint32_t) code; // r15 (pc)
|
||||
*stack-- = 0x14141414; // r14 (lr)
|
||||
*stack-- = org_stack -4; // r13 (sp)
|
||||
*stack-- = 0x12121212; // r12
|
||||
*stack-- = 0x11111111; // r11
|
||||
*stack-- = 0x10101010; // r10
|
||||
*stack-- = 0x09090909; // r9
|
||||
*stack-- = 0x08080808; // r8
|
||||
*stack-- = 0x07070707; // r7
|
||||
*stack-- = 0x06060606; // r6
|
||||
*stack-- = 0x05050505; // r5
|
||||
*stack-- = 0x04040404; // r4
|
||||
*stack-- = 0x03030303; // r3
|
||||
*stack-- = 0x02020202; // r2
|
||||
*stack-- = 0x01010101; // r1
|
||||
*stack-- = (uint32_t) arg; // r0 (function parameter)
|
||||
*stack = 0xF0000013; // SPSR (SVC, ARM, IRQ & FIQ enabled)
|
||||
|
||||
printf("\r");
|
||||
*/
|
||||
return PITC_RESTART_TIMER;
|
||||
return stack;
|
||||
}
|
||||
|
||||
static struct pitc_timer pitc_test_timer = {
|
||||
.interval = 10,
|
||||
.func = &pitc_test,
|
||||
};
|
||||
/* we're in the scheduler, SVC mode */
|
||||
void restore_ctx(uint32_t *stack)
|
||||
{
|
||||
asm volatile (
|
||||
/* restore spsr */
|
||||
"mov lr, r0 \n\t"
|
||||
"ldmia lr!, {r0} \n\t"
|
||||
"msr spsr, r0 \n\t"
|
||||
|
||||
/* restore all registers */
|
||||
"ldmia lr, {r0-r13} \n\t"
|
||||
"nop \n\t"
|
||||
"ldmia sp!, {r14-r15}^ \n\t"
|
||||
"nop \n\t"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void store_ctx(void)
|
||||
{
|
||||
asm volatile (
|
||||
/* push r0 on (task)stack */
|
||||
"stmdb sp!, {r0} \n\r"
|
||||
|
||||
// TODO: fill r0 with address TCB
|
||||
|
||||
/* return address */
|
||||
"stmdb r0!, {lr} \n\r"
|
||||
);
|
||||
}
|
||||
|
||||
void testfunc(void *p)
|
||||
{
|
||||
printf("bla: %p\n\r", p);
|
||||
while(1);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
@ -63,33 +93,13 @@ int main(void)
|
||||
*AT91C_PIOA_PER = LED_GREEN | LED_ORANGE;
|
||||
*AT91C_PIOA_OER = LED_GREEN | LED_ORANGE;
|
||||
|
||||
/* needed for dbgu */
|
||||
at91_sysc_init();
|
||||
|
||||
at91_dbgu_init();
|
||||
at91_dbgu_puts("==========================================================\n\rGood morning Dave\n\r");
|
||||
|
||||
/* triggers pinchange-isrs */
|
||||
at91_pio_init();
|
||||
uint32_t *blub = static_alloc(1024);
|
||||
printf("blub: %p\n\r", blub);
|
||||
|
||||
/* timer */
|
||||
at91_pitc_init();
|
||||
at91_rttc_test_init();
|
||||
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);
|
||||
uint32_t *tmp = init_ctx(blub + (1024/4), testfunc, 0);
|
||||
restore_ctx(tmp);
|
||||
}
|
||||
|
@ -1,92 +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[4];
|
||||
|
||||
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)
|
||||
{
|
||||
*AT91C_ADC_CR = AT91C_ADC_START;
|
||||
return PITC_RESTART_TIMER;
|
||||
}
|
||||
|
||||
static struct pitc_timer adc_timer = {
|
||||
.interval = 100,
|
||||
.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 = 0xF0;
|
||||
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);
|
||||
}
|
@ -59,8 +59,8 @@ ARM7TDMI 32bit Undefined Address Instruction Fetch Abort
|
||||
--- Registerdump (cpsr:0xa0000013 - SVC):
|
||||
r0:0x00000002 r1:0x0000000c r2:0x00200038 r3:0x80000000
|
||||
r4:0x0020fffc r5:0x00101788 r6:0x00000180 r7:0x41000000
|
||||
r8:0x00008400 r9:0x00100004 r10:0x03000294 fp:0x0020fe84
|
||||
r12:0x0020fe10 sp:0x0020fe70 lr:0x00102174 pc:0x80000004
|
||||
r8:0x00008400 r9:0x00100004 sl:0x03000294 fp:0x0020fe84
|
||||
ip:0x0020fe10 sp:0x0020fe70 lr:0x00102174 pc:0x80000004
|
||||
--- Stackdump:
|
||||
0x0020fe70: 0x0020fe7c 0x00101648 0x001015a8 0x0020feaf
|
||||
0x0020fe80: 0x0020fec0 0x0020fe90 0x00101780 0x00101620
|
||||
@ -142,9 +142,9 @@ void at91_abt_handler(uint32_t cpsr, uint32_t *registers)
|
||||
dbgu_hexvar(" r7:", registers[10]);
|
||||
dbgu_hexvar("\n r8:", registers[11]);
|
||||
dbgu_hexvar(" r9:", registers[12]);
|
||||
dbgu_hexvar(" r10:", registers[13]);
|
||||
dbgu_hexvar(" sl:", registers[13]);
|
||||
dbgu_hexvar(" fp:", registers[14]);
|
||||
dbgu_hexvar("\nr12:", registers[15]);
|
||||
dbgu_hexvar("\n ip:", registers[15]);
|
||||
dbgu_hexvar(" sp:", registers[0]);
|
||||
dbgu_hexvar(" lr:", registers[1]);
|
||||
dbgu_hexvar(" pc:", registers[2]);
|
||||
|
@ -1,69 +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_pio.h"
|
||||
|
||||
/* extern symbols, defined in ldscript */
|
||||
extern struct pio_pinchange_isr _pio_isr_table;
|
||||
extern struct pio_pinchange_isr _pio_isr_table_end;
|
||||
|
||||
static void pio_isr(void)
|
||||
{
|
||||
uint32_t status = *AT91C_PIOA_ISR;
|
||||
uint32_t input = *AT91C_PIOA_PDSR;
|
||||
|
||||
struct pio_pinchange_isr *isr;
|
||||
for (isr = &_pio_isr_table; isr < &_pio_isr_table_end; isr++)
|
||||
if (isr->mask & status)
|
||||
isr->func(status, input);
|
||||
}
|
||||
|
||||
void pio_trigger_isr(uint32_t mask)
|
||||
{
|
||||
uint32_t input = *AT91C_PIOA_PDSR;
|
||||
|
||||
struct pio_pinchange_isr *isr;
|
||||
for (isr = &_pio_isr_table; isr < &_pio_isr_table_end; isr++)
|
||||
if (isr->mask & mask)
|
||||
isr->func(mask, input);
|
||||
}
|
||||
|
||||
void at91_pio_init(void)
|
||||
{
|
||||
/* enable PIO clock */
|
||||
*AT91C_PMC_PCER = (1 << AT91C_ID_PIOA);
|
||||
|
||||
/* enable pinchange interrupts */
|
||||
struct pio_pinchange_isr *isr;
|
||||
for (isr = &_pio_isr_table; isr < &_pio_isr_table_end; isr++)
|
||||
*AT91C_PIOA_IER = isr->mask;
|
||||
|
||||
/* dummy read to clear interrupts */
|
||||
uint32_t dummy = *AT91C_PIOA_ISR;
|
||||
dummy = dummy;
|
||||
|
||||
/* low priority, level triggered, own vector */
|
||||
AT91S_AIC *aic = AT91C_BASE_AIC;
|
||||
aic->AIC_SMR[AT91C_ID_PIOA] = IRQPRIO_PIOA | AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL;
|
||||
aic->AIC_SVR[AT91C_ID_PIOA] = (uint32_t)pio_isr;
|
||||
aic->AIC_IECR = (1 << AT91C_ID_PIOA);
|
||||
}
|
@ -1,79 +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 "AT91SAM7S256.h"
|
||||
#include "at91_pitc.h"
|
||||
#include "at91_sysc.h"
|
||||
#include "board.h"
|
||||
|
||||
/* PIV is 20bit -> min. 3Hz @48MHz MCK */
|
||||
#define HZ_TO_PIV(HZ) (MCK / (16 * HZ))
|
||||
|
||||
static LIST_HEAD(timer_list);
|
||||
volatile static uint32_t pitc_ticks;
|
||||
|
||||
void pitc_schedule_timer(struct pitc_timer *timer)
|
||||
{
|
||||
timer->nextrun = timer->interval + pitc_ticks;
|
||||
|
||||
struct pitc_timer *search;
|
||||
list_for_each_entry(search, &timer_list, list)
|
||||
if (search->nextrun > timer->nextrun)
|
||||
break;
|
||||
|
||||
list_add_tail(&timer->list, &search->list);
|
||||
}
|
||||
|
||||
static void pitc_isr(uint32_t status)
|
||||
{
|
||||
/* get Ticks and clear interrupt */
|
||||
pitc_ticks += (*AT91C_PITC_PIVR & AT91C_PITC_PICNT) >> 20;
|
||||
|
||||
struct pitc_timer *search, *tmp;
|
||||
list_for_each_entry_safe(search, tmp, &timer_list, list) {
|
||||
/* if this entry not scheduled yet, abort search */
|
||||
if (pitc_ticks < search->nextrun)
|
||||
break;
|
||||
|
||||
/* remove from list */
|
||||
list_del(&search->list);
|
||||
|
||||
/* exec handler */
|
||||
if (search->func(search) == PITC_REMOVE_TIMER) {
|
||||
/* one-shot timer, mark as completed */
|
||||
search->nextrun = 0x00;
|
||||
continue;
|
||||
}
|
||||
/* interval timer, reschedule it */
|
||||
pitc_schedule_timer(search);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t pitc_get_ticks(void)
|
||||
{
|
||||
return pitc_ticks;
|
||||
}
|
||||
|
||||
void at91_pitc_init(void)
|
||||
{
|
||||
sysc_register_isr(AT91_SYSIRQ_PIT, &pitc_isr);
|
||||
|
||||
*AT91C_PITC_PIMR = (AT91C_PITC_PIV & HZ_TO_PIV(100)) |
|
||||
AT91C_PITC_PITEN |
|
||||
AT91C_PITC_PITIEN;
|
||||
}
|
@ -1,34 +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);
|
||||
}
|
227
src/at91_tc1.c
227
src/at91_tc1.c
@ -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);
|
261
src/at91_twi.c
261
src/at91_twi.c
@ -1,261 +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 <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "AT91SAM7S256.h"
|
||||
#include "board.h"
|
||||
#include "at91_twi.h"
|
||||
#include "at91_pio.h"
|
||||
|
||||
/*
|
||||
* undocumented TWI_SR flags, at least OVRE seems to be present on a sam7s256
|
||||
* taken from linux-2.6.24/include/asm-arm/arch-at91/at91_twi.h
|
||||
*/
|
||||
#define AT91_TWI_OVRE (1<<6) /* Overrun */
|
||||
#define AT91_TWI_UNRE (1<<7) /* Underrun */
|
||||
|
||||
/*
|
||||
* while ((cldiv = ((MCK / (2 * TWI)) -3) / (1 << ckdiv)) > 255)
|
||||
* ckdiv++ ;
|
||||
*
|
||||
* works for TWI >= 100kHz
|
||||
*/
|
||||
#define TWI_CLK(x) (((MCK / (2 * (x))) -3)<<8 | ((MCK / (2 * (x))) -3))
|
||||
|
||||
enum twi_states {
|
||||
TWI_IDLE = 0x00,
|
||||
TWI_ERROR,
|
||||
TWI_GENERIC_CMD = 0x10,
|
||||
TWI_BLMC_UPDATE = 0x20,
|
||||
};
|
||||
|
||||
static volatile uint32_t twi_state = TWI_IDLE;
|
||||
static uint8_t *twi_data;
|
||||
static uint32_t twi_size;
|
||||
static uint32_t twi_count;
|
||||
|
||||
static void twi_isr(void)
|
||||
{
|
||||
/* get status */
|
||||
uint32_t status = *AT91C_TWI_SR;
|
||||
status &= *AT91C_TWI_IMR;
|
||||
|
||||
/* NACK - disable all interrupts and go to state TWI_ERROR */
|
||||
if (status & AT91C_TWI_NACK) {
|
||||
*AT91C_TWI_IDR = AT91C_TWI_TXCOMP | AT91C_TWI_RXRDY | AT91C_TWI_TXRDY | AT91C_TWI_NACK;
|
||||
twi_state = TWI_ERROR;
|
||||
|
||||
// TODO: TWI_BLMC_UPDATE?
|
||||
return;
|
||||
}
|
||||
|
||||
/* tx register ready for new data */
|
||||
if (status & AT91C_TWI_TXRDY) {
|
||||
if (twi_count != twi_size) {
|
||||
/* feed next byte */
|
||||
*AT91C_TWI_THR = twi_data[twi_count++];
|
||||
|
||||
} else {
|
||||
/* wait for TXCOMP */
|
||||
*AT91C_TWI_IDR = AT91C_TWI_RXRDY | AT91C_TWI_TXRDY;
|
||||
*AT91C_TWI_IER = AT91C_TWI_TXCOMP | AT91C_TWI_NACK;
|
||||
}
|
||||
}
|
||||
|
||||
/* rx register has data */
|
||||
if (status & AT91C_TWI_RXRDY) {
|
||||
/* get data */
|
||||
twi_data[twi_count++] = *AT91C_TWI_RHR;
|
||||
|
||||
/* transfer complete? */
|
||||
if (twi_count == twi_size) {
|
||||
/* send STOP and wait for TXCOMP */
|
||||
*AT91C_TWI_CR = AT91C_TWI_STOP;
|
||||
*AT91C_TWI_IDR = AT91C_TWI_TXRDY;
|
||||
*AT91C_TWI_IER = AT91C_TWI_TXCOMP | AT91C_TWI_NACK;
|
||||
}
|
||||
}
|
||||
|
||||
/* transfer really complete? */
|
||||
if (status & AT91C_TWI_TXCOMP) {
|
||||
|
||||
/* are we doing a blmc update? */
|
||||
if (twi_state == TWI_BLMC_UPDATE) {
|
||||
uint32_t addr = (*AT91C_TWI_MMR >> 16) & 0x7F;
|
||||
if (addr != TWI_ADDR_BL4) {
|
||||
/* increase address */
|
||||
*AT91C_TWI_MMR += (1<<16);
|
||||
|
||||
/* send next value to next blmc */
|
||||
*AT91C_TWI_THR = *twi_data++;
|
||||
} else {
|
||||
// TODO:
|
||||
}
|
||||
|
||||
} else {
|
||||
*AT91C_TWI_IDR = AT91C_TWI_TXCOMP | AT91C_TWI_RXRDY | AT91C_TWI_TXRDY | AT91C_TWI_NACK;
|
||||
twi_state = TWI_IDLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t twi_setpwm(uint8_t *values)
|
||||
{
|
||||
if (twi_state != TWI_IDLE)
|
||||
return 1;
|
||||
|
||||
twi_state = TWI_BLMC_UPDATE;
|
||||
twi_data = values; /* data is not copied! */
|
||||
twi_size = 0;
|
||||
twi_count = 0;
|
||||
|
||||
*AT91C_TWI_MMR = (TWI_ADDR_BL1 << 16) | AT91C_TWI_IADRSZ_1_BYTE;
|
||||
*AT91C_TWI_IADR = CMD_SET_PWM;
|
||||
|
||||
*AT91C_TWI_THR = *twi_data++;
|
||||
*AT91C_TWI_IER = AT91C_TWI_TXRDY | AT91C_TWI_NACK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t twi_cmd(uint8_t addr, struct blmc_cmd *cmd)
|
||||
{
|
||||
if (twi_state != TWI_IDLE)
|
||||
return 1;
|
||||
|
||||
/* TODO: locking needed? */
|
||||
twi_state = TWI_GENERIC_CMD;
|
||||
|
||||
/* read transfer, or write transfer with payload */
|
||||
if (cmd->mode & BLMC_CMD_READ || cmd->size != 0) {
|
||||
/* set address, direction, argument count and command bytes */
|
||||
*AT91C_TWI_MMR = (addr << 16) | (cmd->mode & 0xFF) << 8;
|
||||
*AT91C_TWI_IADR = cmd->cmd;
|
||||
|
||||
/* write transfer without payload */
|
||||
} else {
|
||||
/* use one cmd byte as payload (needed to start transfer) */
|
||||
cmd->mode--;
|
||||
*AT91C_TWI_MMR = (addr << 16) | (cmd->mode & 0xFF) << 8;
|
||||
*AT91C_TWI_IADR = (cmd->cmd) >> 8;
|
||||
}
|
||||
|
||||
/* isr needs data & size parameters */
|
||||
twi_data = cmd->data;
|
||||
twi_size = cmd->size;
|
||||
twi_count = 0;
|
||||
|
||||
if (cmd->mode & BLMC_CMD_READ) {
|
||||
*AT91C_TWI_CR = AT91C_TWI_START;
|
||||
*AT91C_TWI_IER = AT91C_TWI_RXRDY | AT91C_TWI_NACK;
|
||||
|
||||
} else {
|
||||
*AT91C_TWI_THR = (twi_size != 0) ? cmd->data[twi_count++] : (cmd->cmd & 0xFF);
|
||||
*AT91C_TWI_IER = AT91C_TWI_TXRDY | AT91C_TWI_NACK;
|
||||
}
|
||||
|
||||
/*
|
||||
* wait for end
|
||||
* TODO: locking needed?
|
||||
* TODO: timeout?
|
||||
*/
|
||||
while (twi_state != TWI_IDLE && twi_state != TWI_ERROR);
|
||||
if (twi_state != TWI_IDLE) {
|
||||
twi_state = TWI_IDLE;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void at91_twi_test(void)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = TWI_ADDR_BL1; i <= TWI_ADDR_BL4; i++) {
|
||||
struct blmc_cmd cmd = {
|
||||
.cmd = CMD_BOOT_LOADER,
|
||||
.mode = BLMC_CMD_WRITE | BLMC_CMD_0_ARG,
|
||||
};
|
||||
uint32_t ret = twi_cmd(i, &cmd);
|
||||
printf("twi[0x%02lx](%ld) ", i, ret);
|
||||
|
||||
volatile uint32_t x;
|
||||
for (x = 0; x < 200000; x++);
|
||||
|
||||
uint8_t buf[16];
|
||||
cmd.cmd = CMD_GET_INFO,
|
||||
cmd.mode = BLMC_CMD_READ | BLMC_CMD_0_ARG,
|
||||
cmd.size = sizeof(buf),
|
||||
cmd.data = buf,
|
||||
ret = twi_cmd(i, &cmd);
|
||||
printf("boot(%ld):'%s' ", ret, buf);
|
||||
|
||||
cmd.cmd = CMD_GET_SIGNATURE;
|
||||
cmd.size = 4;
|
||||
ret = twi_cmd(i, &cmd);
|
||||
printf("sig(%ld):0x%02x%02x%02x ", ret, buf[0], buf[1], buf[2]);
|
||||
|
||||
cmd.cmd = CMD_BOOT_APPLICATION;
|
||||
cmd.mode = BLMC_CMD_WRITE | BLMC_CMD_0_ARG;
|
||||
cmd.size = 0;
|
||||
ret = twi_cmd(i, &cmd);
|
||||
|
||||
for (x = 0; x < 200000; x++);
|
||||
|
||||
cmd.cmd = CMD_GET_INFO,
|
||||
cmd.mode = BLMC_CMD_READ | BLMC_CMD_0_ARG,
|
||||
cmd.size = sizeof(buf),
|
||||
cmd.data = buf,
|
||||
ret = twi_cmd(i, &cmd);
|
||||
printf("app(%ld):'%s'\n\r", ret, buf);
|
||||
}
|
||||
}
|
||||
|
||||
void at91_twi_init(void)
|
||||
{
|
||||
/* enable Clock */
|
||||
*AT91C_PMC_PCER = (1 << AT91C_ID_TWI);
|
||||
|
||||
/* SDA & SCL from Peripheral A, Open Drain, no Pullup */
|
||||
AT91S_PIO *pio = AT91C_BASE_PIOA;
|
||||
|
||||
/* do a software reset (bus not connected) */
|
||||
*AT91C_TWI_CR = AT91C_TWI_SWRST;
|
||||
|
||||
pio->PIO_MDER = AT91C_PA3_TWD | AT91C_PA4_TWCK;
|
||||
pio->PIO_PPUDR = AT91C_PA3_TWD | AT91C_PA4_TWCK;
|
||||
pio->PIO_ASR = AT91C_PA3_TWD | AT91C_PA4_TWCK;
|
||||
pio->PIO_PDR = AT91C_PA3_TWD | AT91C_PA4_TWCK;
|
||||
|
||||
/* set TWI Clock */
|
||||
*AT91C_TWI_CWGR = TWI_CLK(400000); //| (5<<16);
|
||||
|
||||
/* disable all (known) interrupts */
|
||||
*AT91C_TWI_IDR = AT91C_TWI_TXCOMP | AT91C_TWI_RXRDY | AT91C_TWI_TXRDY | AT91C_TWI_NACK;
|
||||
|
||||
/* level triggered, own vector */
|
||||
AT91S_AIC *aic = AT91C_BASE_AIC;
|
||||
aic->AIC_SMR[AT91C_ID_TWI] = IRQPRIO_TWI | AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL;
|
||||
aic->AIC_SVR[AT91C_ID_TWI] = (uint32_t)twi_isr;
|
||||
aic->AIC_IECR = (1 << AT91C_ID_TWI);
|
||||
|
||||
/* enable teh monster */
|
||||
*AT91C_TWI_CR = AT91C_TWI_MSEN;
|
||||
}
|
552
src/at91_udp.c
552
src/at91_udp.c
@ -1,552 +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 <stdio.h>
|
||||
#include "AT91SAM7S256.h"
|
||||
#include "at91_pio.h"
|
||||
#include "board.h"
|
||||
#include "fifo.h"
|
||||
|
||||
#include "usb_ch9.h"
|
||||
#include "usb_cdc.h"
|
||||
|
||||
#define csr_clear_flags(csr, flags) \
|
||||
while ((csr) & (flags)) \
|
||||
(csr) &= ~(flags);
|
||||
|
||||
#define csr_set_flags(csr, flags) \
|
||||
while (((csr) & (flags)) != (flags)) \
|
||||
(csr) |= (flags);
|
||||
|
||||
struct ep_transfer {
|
||||
uint16_t length;
|
||||
uint16_t curpos;
|
||||
|
||||
char *data;
|
||||
void (*complete_cb)(void);
|
||||
};
|
||||
|
||||
struct ep_ctx {
|
||||
uint16_t maxpktsize;
|
||||
uint16_t flags;
|
||||
|
||||
union {
|
||||
struct ep_transfer *transfer;
|
||||
struct fifo *fifo;
|
||||
};
|
||||
};
|
||||
|
||||
#define CTX_TRANSFER 0x01 /* ctx use ep_transfer struct */
|
||||
#define CTX_FIFO 0x02 /* ctx use fifo */
|
||||
#define CTX_IN 0x04 /* write to the host */
|
||||
#define CTX_OUT 0x08 /* read from the host */
|
||||
#define CTX_RXBANK0 0x10
|
||||
#define CTX_RXBANK1 0x20
|
||||
|
||||
static struct ep_transfer ep0_transfer;
|
||||
static struct ep_ctx ep_ctx[4];
|
||||
|
||||
static uint16_t current_address;
|
||||
static uint16_t current_config;
|
||||
static uint16_t current_interface;
|
||||
|
||||
static const struct usb_device_descriptor dev_descriptor = {
|
||||
.bLength = sizeof(struct usb_device_descriptor),
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
.bcdUSB = 0x0110,
|
||||
.bMaxPacketSize0 = 8,
|
||||
.idVendor = USB_VENDOR_ID,
|
||||
.idProduct = USB_PRODUCT_ID +1,
|
||||
.bcdDevice = 0x0001,
|
||||
.bNumConfigurations = 1,
|
||||
};
|
||||
|
||||
struct my_config {
|
||||
struct usb_config_descriptor cfg;
|
||||
struct usb_interface_descriptor ctrl_iface;
|
||||
struct usb_cdc_header_desc cdc_header;
|
||||
struct usb_cdc_call_mgmt_descriptor cdc_call_mgmt;
|
||||
struct usb_cdc_acm_descriptor cdc_acm;
|
||||
struct usb_cdc_union_desc cdc_union;
|
||||
struct usb_endpoint_descriptor notify_ep;
|
||||
struct usb_interface_descriptor data_iface;
|
||||
struct usb_endpoint_descriptor dataout_ep;
|
||||
struct usb_endpoint_descriptor datain_ep;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
static const struct my_config cfg_descriptor = {
|
||||
.cfg = {
|
||||
.bLength = sizeof(struct usb_config_descriptor),
|
||||
.bDescriptorType = USB_DT_CONFIG,
|
||||
.wTotalLength = sizeof(struct my_config),
|
||||
.bNumInterfaces = 2,
|
||||
.bConfigurationValue = 1,
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER | USB_CONFIG_ATT_WAKEUP,
|
||||
.bMaxPower = 50,
|
||||
},
|
||||
.ctrl_iface = {
|
||||
.bLength = sizeof(struct usb_interface_descriptor),
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = 0,
|
||||
.bNumEndpoints = 1,
|
||||
.bInterfaceClass = USB_CLASS_COMM,
|
||||
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
|
||||
.bInterfaceProtocol = 1,
|
||||
},
|
||||
.cdc_header = {
|
||||
.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),
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = 1,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = USB_CLASS_CDC_DATA,
|
||||
},
|
||||
.dataout_ep = {
|
||||
.bLength = sizeof(struct usb_endpoint_descriptor),
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_OUT | 0x01,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 64,
|
||||
},
|
||||
.datain_ep = {
|
||||
.bLength = sizeof(struct usb_endpoint_descriptor),
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN | 0x02,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 64,
|
||||
},
|
||||
};
|
||||
|
||||
static void ep_transfer_send(uint32_t ep, char *data, uint32_t length,
|
||||
void (*complete_cb)(void))
|
||||
{
|
||||
struct ep_ctx *ctx = &ep_ctx[ep];
|
||||
// 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)))
|
||||
return;
|
||||
|
||||
/* from buffer to usb */
|
||||
ctx->flags |= CTX_IN;
|
||||
|
||||
struct ep_transfer *transfer = ctx->transfer;
|
||||
transfer->length = length;
|
||||
transfer->curpos = 0;
|
||||
transfer->data = data;
|
||||
transfer->complete_cb = complete_cb;
|
||||
|
||||
uint32_t maxsize = ctx->maxpktsize;
|
||||
|
||||
/* get data from transfer */
|
||||
while (transfer->curpos < transfer->length && maxsize--)
|
||||
AT91C_UDP_FDR[ep] = transfer->data[transfer->curpos++];
|
||||
|
||||
/* trigger tx */
|
||||
AT91C_UDP_CSR[ep] |= AT91C_UDP_TXPKTRDY;
|
||||
}
|
||||
|
||||
static void ep_transfer_receive(uint32_t ep, char *data, uint32_t length,
|
||||
void (*complete_cb)(void))
|
||||
{
|
||||
struct ep_ctx *ctx = &ep_ctx[ep];
|
||||
// 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)))
|
||||
return;
|
||||
|
||||
/* from usb to buffer */
|
||||
ctx->flags |= CTX_OUT;
|
||||
|
||||
struct ep_transfer *transfer = ctx->transfer;
|
||||
transfer->length = length;
|
||||
transfer->curpos = 0;
|
||||
transfer->data = data;
|
||||
transfer->complete_cb = complete_cb;
|
||||
}
|
||||
|
||||
/* stalls the endpoint */
|
||||
static void ep_send_stall(uint32_t ep)
|
||||
{
|
||||
printf("stall\n\r");
|
||||
AT91C_UDP_CSR[ep] |= AT91C_UDP_FORCESTALL;
|
||||
}
|
||||
|
||||
static void udp_configure_ep(const struct usb_endpoint_descriptor *desc)
|
||||
{
|
||||
/* get endpoint address, set Max Packet Size */
|
||||
uint32_t ep = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
||||
ep_ctx[ep].maxpktsize = desc->wMaxPacketSize;
|
||||
|
||||
/* get endpoint type (ctrl, iso, bulb, int) */
|
||||
uint32_t eptype = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
|
||||
if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
|
||||
eptype |= 0x04;
|
||||
} else {
|
||||
ep_ctx[ep].flags |= CTX_RXBANK0;
|
||||
}
|
||||
|
||||
/* configure UDP endpoint and enable interrupt */
|
||||
AT91C_UDP_CSR[ep] = AT91C_UDP_EPEDS | (eptype << 8);
|
||||
*AT91C_UDP_IER = (1 << ep);
|
||||
}
|
||||
|
||||
/*
|
||||
* set local address
|
||||
* (USB_REQ_SET_ADDRESS callback)
|
||||
*/
|
||||
static void udp_txcb_setaddress(void)
|
||||
{
|
||||
*AT91C_UDP_FADDR = (AT91C_UDP_FEN | current_address);
|
||||
*AT91C_UDP_GLBSTATE = AT91C_UDP_FADDEN;
|
||||
}
|
||||
|
||||
/*
|
||||
* configure endpoints
|
||||
* (USB_REQ_SET_CONFIGURATION callback)
|
||||
*/
|
||||
static void udp_txcb_setconfig(void)
|
||||
{
|
||||
udp_configure_ep(&cfg_descriptor.notify_ep);
|
||||
udp_configure_ep(&cfg_descriptor.datain_ep);
|
||||
udp_configure_ep(&cfg_descriptor.dataout_ep);
|
||||
|
||||
/* set UDP to "configured" */
|
||||
*AT91C_UDP_GLBSTATE = AT91C_UDP_CONFG;
|
||||
}
|
||||
|
||||
static void udp_txcb_setinterface(void)
|
||||
{
|
||||
printf("claim interface %d\n\r", current_interface);
|
||||
}
|
||||
|
||||
static void ep_handle_ctrlrequest(struct usb_ctrlrequest *req)
|
||||
{
|
||||
printf("typ:0x%02x req:0x%02x val:0x%04x idx:0x%04x len:0x%04x\n\r",
|
||||
req->bRequestType, req->bRequest, req->wValue, req->wIndex, req->wLength);
|
||||
|
||||
switch (req->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK)) {
|
||||
case (USB_TYPE_STANDARD | USB_RECIP_DEVICE): /* 0x00/0x80 */
|
||||
switch (req->bRequest) {
|
||||
case USB_REQ_SET_ADDRESS: /* 0x05 */
|
||||
current_address = req->wValue;
|
||||
ep_transfer_send(0, NULL, 0, udp_txcb_setaddress);
|
||||
break;
|
||||
|
||||
case USB_REQ_GET_DESCRIPTOR: /* 0x06 */
|
||||
switch (req->wValue >> 8) {
|
||||
case USB_DT_DEVICE: /* 0x01 */
|
||||
ep_transfer_send(0, (char *)&dev_descriptor,
|
||||
MIN(sizeof(dev_descriptor), req->wLength),
|
||||
NULL);
|
||||
break;
|
||||
|
||||
case USB_DT_CONFIG: /* 0x02 */
|
||||
ep_transfer_send(0, (char *)&cfg_descriptor,
|
||||
MIN(sizeof(cfg_descriptor), req->wLength),
|
||||
NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
ep_send_stall(0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_REQ_SET_CONFIGURATION: /* 0x09 */
|
||||
current_config = req->wValue;
|
||||
ep_transfer_send(0, NULL, 0, udp_txcb_setconfig);
|
||||
break;
|
||||
|
||||
default:
|
||||
ep_send_stall(0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case (USB_TYPE_STANDARD | USB_RECIP_INTERFACE): /* 0x01/0x81 */
|
||||
// TODO: follow current_interface
|
||||
switch (req->bRequest) {
|
||||
case USB_REQ_SET_INTERFACE: /* 0x0b */
|
||||
current_interface = req->wValue;
|
||||
ep_transfer_send(0, NULL, 0, udp_txcb_setinterface);
|
||||
break;
|
||||
|
||||
default:
|
||||
ep_send_stall(0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case (USB_TYPE_CLASS | USB_RECIP_INTERFACE): /* 0x21/0xA1 */
|
||||
// TODO: follow current_interface
|
||||
switch (req->bRequest) {
|
||||
case USB_CDC_REQ_SET_LINE_CODING: /* 0x20 */
|
||||
/* read 7 bytes */
|
||||
break;
|
||||
|
||||
case USB_CDC_REQ_SET_CONTROL_LINE_STATE: /* 0x22 */
|
||||
ep_transfer_send(0, NULL, 0, NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
ep_send_stall(0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ep_send_stall(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void udp_handle_ep(uint32_t ep)
|
||||
{
|
||||
/* endpoint enabled? */
|
||||
AT91_REG *csr = &AT91C_UDP_CSR[ep];
|
||||
if (!(*csr & AT91C_UDP_EPEDS))
|
||||
return;
|
||||
|
||||
/* ctrl request packet? */
|
||||
if (*csr & AT91C_UDP_RXSETUP) {
|
||||
struct usb_ctrlrequest req;
|
||||
uint8_t *p;
|
||||
for (p = (uint8_t *)&req; p < (uint8_t *)(&req +1); p++)
|
||||
*p = AT91C_UDP_FDR[ep];
|
||||
|
||||
/* set data phase transfer direction */
|
||||
if (req.bRequestType & USB_DIR_IN)
|
||||
*csr |= AT91C_UDP_DIR;
|
||||
|
||||
/* clear interrupt - *MUST* use csr_clear_flags() here */
|
||||
csr_clear_flags(*csr, AT91C_UDP_RXSETUP);
|
||||
|
||||
ep_handle_ctrlrequest(&req);
|
||||
}
|
||||
|
||||
/* transmit complete? */
|
||||
if (*csr & AT91C_UDP_TXCOMP) {
|
||||
struct ep_ctx *ctx = &ep_ctx[ep];
|
||||
|
||||
if (ctx->flags & CTX_FIFO) {
|
||||
/* get data from fifo */
|
||||
if (fifo_txudp(ctx->fifo, ep, ctx->maxpktsize))
|
||||
AT91C_UDP_CSR[ep] |= AT91C_UDP_TXPKTRDY;
|
||||
|
||||
} else if ((ctx->flags & (CTX_TRANSFER | CTX_IN)) == (CTX_TRANSFER | CTX_IN)) {
|
||||
/* transfer not complete */
|
||||
struct ep_transfer *transfer = ctx->transfer;
|
||||
if (transfer->length != transfer->curpos) {
|
||||
uint32_t maxsize = ctx->maxpktsize;
|
||||
|
||||
/* get data from transfer */
|
||||
while (transfer->curpos < transfer->length && maxsize--)
|
||||
AT91C_UDP_FDR[ep] = transfer->data[transfer->curpos++];
|
||||
|
||||
/* trigger tx */
|
||||
AT91C_UDP_CSR[ep] |= AT91C_UDP_TXPKTRDY;
|
||||
|
||||
/* transfer complete, execute callback */
|
||||
} else {
|
||||
ctx->flags &= ~CTX_IN;
|
||||
|
||||
if (transfer->complete_cb)
|
||||
transfer->complete_cb();
|
||||
}
|
||||
}
|
||||
|
||||
/* clear interrupt */
|
||||
*csr &= ~(AT91C_UDP_TXCOMP);
|
||||
}
|
||||
|
||||
/* clear STALLSENT interrupt */
|
||||
if (*csr & AT91C_UDP_STALLSENT)
|
||||
csr_clear_flags(*csr, (AT91C_UDP_STALLSENT | AT91C_UDP_FORCESTALL));
|
||||
|
||||
/* data ready to read? */
|
||||
if (*csr & (AT91C_UDP_RX_DATA_BK0 | AT91C_UDP_RX_DATA_BK1)) {
|
||||
struct ep_ctx *ctx = &ep_ctx[ep];
|
||||
|
||||
uint16_t len = (*csr & AT91C_UDP_RXBYTECNT) >> 16;
|
||||
|
||||
// TODO: only ep0 status OUT?
|
||||
if (!len && (ctx->flags & CTX_TRANSFER)) {
|
||||
ctx->flags &= ~(CTX_OUT | CTX_IN);
|
||||
ctx->transfer->length = 0;
|
||||
ctx->transfer->curpos = 0;
|
||||
}
|
||||
|
||||
if (ctx->flags & CTX_FIFO) {
|
||||
fifo_rxudp(ctx->fifo, ep, len);
|
||||
|
||||
} else if ((ctx->flags & (CTX_TRANSFER | CTX_OUT)) == (CTX_TRANSFER | CTX_OUT)) {
|
||||
|
||||
/* transfer not complete */
|
||||
struct ep_transfer *transfer = ctx->transfer;
|
||||
if (transfer->length != transfer->curpos) {
|
||||
/* get data from transfer */
|
||||
while (transfer->curpos < transfer->length && len--)
|
||||
transfer->data[transfer->curpos++] = AT91C_UDP_FDR[ep];
|
||||
}
|
||||
|
||||
/* test again */
|
||||
if (transfer->length == transfer->curpos) {
|
||||
ctx->flags &= ~CTX_OUT;
|
||||
|
||||
if (transfer->complete_cb)
|
||||
transfer->complete_cb();
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->flags & CTX_RXBANK0) {
|
||||
if (*csr & AT91C_UDP_RX_DATA_BK0)
|
||||
csr_clear_flags(*csr, AT91C_UDP_RX_DATA_BK0);
|
||||
|
||||
/* all but ep0 have ping pong buffers */
|
||||
if (ep > 0)
|
||||
ctx->flags = (ctx->flags & ~CTX_RXBANK0) | CTX_RXBANK1;
|
||||
|
||||
} else if (ctx->flags & CTX_RXBANK1) {
|
||||
if (*csr & AT91C_UDP_RX_DATA_BK1)
|
||||
csr_clear_flags(*csr, AT91C_UDP_RX_DATA_BK1);
|
||||
|
||||
ctx->flags = (ctx->flags & ~CTX_RXBANK1) | CTX_RXBANK0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void udp_isr(void)
|
||||
{
|
||||
uint32_t isr = *AT91C_UDP_ISR;
|
||||
if (isr & AT91C_UDP_ENDBUSRES) {
|
||||
AT91S_UDP *udp = AT91C_BASE_UDP;
|
||||
|
||||
/* reset all endpoints */
|
||||
udp->UDP_RSTEP = (AT91C_UDP_EP0 | AT91C_UDP_EP1 |
|
||||
AT91C_UDP_EP2 | AT91C_UDP_EP3) ;
|
||||
udp->UDP_RSTEP = 0;
|
||||
|
||||
/* init ep0 */
|
||||
struct ep_ctx *ctx = &ep_ctx[0];
|
||||
ctx->maxpktsize = 8;
|
||||
ctx->flags = CTX_TRANSFER | CTX_RXBANK0;
|
||||
ctx->transfer = &ep0_transfer;
|
||||
ctx->transfer->length = 0;
|
||||
ctx->transfer->curpos = 0;
|
||||
|
||||
/* Configure endpoint0 as Control EP */
|
||||
udp->UDP_CSR[0] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_CTRL);
|
||||
|
||||
/* enable ep0 Interrupt, disable all others */
|
||||
udp->UDP_IER = AT91C_UDP_EPINT0;
|
||||
udp->UDP_IDR = AT91C_UDP_EPINT1 | AT91C_UDP_EPINT2 | AT91C_UDP_EPINT3 |
|
||||
AT91C_UDP_RXSUSP | AT91C_UDP_RXRSM | AT91C_UDP_SOFINT |
|
||||
AT91C_UDP_WAKEUP;
|
||||
}
|
||||
|
||||
/* Handle Endpoint Interrupts */
|
||||
uint32_t i;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (isr & *AT91C_UDP_IMR & (1<<i))
|
||||
udp_handle_ep(i);
|
||||
}
|
||||
|
||||
/* clear all unhandled interrupts */
|
||||
*AT91C_UDP_ICR = isr & (AT91C_UDP_RXSUSP | AT91C_UDP_RXRSM |
|
||||
AT91C_UDP_ENDBUSRES | AT91C_UDP_WAKEUP);
|
||||
}
|
||||
|
||||
void at91_udp_init(void)
|
||||
{
|
||||
/* configure & disable Pullup, disable Pullup von VBUS */
|
||||
AT91PS_PIO pio = AT91C_BASE_PIOA;
|
||||
pio->PIO_CODR = UDP_PULLUP;
|
||||
pio->PIO_PER = UDP_PULLUP;
|
||||
pio->PIO_OER = UDP_PULLUP;
|
||||
// TODO: needed?
|
||||
pio->PIO_PPUDR = UDP_VBUS_MON;
|
||||
|
||||
/* UDPCK (48MHz) = PLLCK / 2 */
|
||||
*AT91C_CKGR_PLLR |= AT91C_CKGR_USBDIV_1;
|
||||
|
||||
/* enable UDP clocks */
|
||||
*AT91C_PMC_SCER = AT91C_PMC_UDP;
|
||||
*AT91C_PMC_PCER = (1 << AT91C_ID_UDP);
|
||||
|
||||
/* enable transmitter */
|
||||
*AT91C_UDP_TXVC &= ~AT91C_UDP_TXVDIS;
|
||||
|
||||
/* clear & disable all UDP interrupts */
|
||||
*AT91C_UDP_IDR = AT91C_UDP_EPINT0 | AT91C_UDP_EPINT1 | AT91C_UDP_EPINT2 |
|
||||
AT91C_UDP_EPINT3 | AT91C_UDP_RXSUSP | AT91C_UDP_RXRSM |
|
||||
AT91C_UDP_SOFINT | AT91C_UDP_WAKEUP;
|
||||
|
||||
*AT91C_UDP_ICR = AT91C_UDP_RXSUSP | AT91C_UDP_RXRSM | AT91C_UDP_SOFINT |
|
||||
AT91C_UDP_ENDBUSRES | AT91C_UDP_WAKEUP ;
|
||||
|
||||
/* level triggered, own vector */
|
||||
AT91S_AIC *aic = AT91C_BASE_AIC;
|
||||
aic->AIC_SMR[AT91C_ID_UDP] = IRQPRIO_UDP | AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL;
|
||||
aic->AIC_SVR[AT91C_ID_UDP] = (uint32_t)udp_isr;
|
||||
aic->AIC_IECR = (1 << AT91C_ID_UDP);
|
||||
|
||||
pio_trigger_isr(UDP_VBUS_MON);
|
||||
}
|
||||
|
||||
static void udp_vbus_monitor(uint32_t status, uint32_t input)
|
||||
{
|
||||
if (input & UDP_VBUS_MON)
|
||||
/* usb connected -> enable pullup */
|
||||
*AT91C_PIOA_SODR = UDP_PULLUP;
|
||||
else
|
||||
/* usb got diconnected -> disable pullup */
|
||||
*AT91C_PIOA_CODR = UDP_PULLUP;
|
||||
}
|
||||
|
||||
PIO_PINCHANGE_ISR(UDP_VBUS_MON, udp_vbus_monitor);
|
1
src/rtos/context.c
Normal file
1
src/rtos/context.c
Normal file
@ -0,0 +1 @@
|
||||
#include <stdint.h>
|
255
src/telemetrie.c
255
src/telemetrie.c
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user