необходимо реализовать связь между виртуальным COM-портом (по USB) реализованным при помощи стандартного примера usb-device-cdc-serial-project-at91sam7s-ek и SPI протоколом...
PLL подвинчен для тактирования ПЛИСки, работающей со своими девайсами по различным протоколам.
связь между ПЛИС и МК по SPI.
проблема в основном стоит в подъеме SPI без команд (такая реализация в примере атмела) и прикручиванию к SPI собственно USB.
т.е. необходим в некотором роде конвертер SPI<->USB (в камне) / USB<->COM (в компе)
итак, переходим к нашим баранам
PLL реализован так:
CODE
void PLL_Clock3(unsigned long frequency)
{
unsigned long freq;
freq = BOARD_MCK/frequency;
AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_PWMC);
AT91C_BASE_PWMC->PWMC_DIS = 0xffffffff;
AT91C_BASE_PIOA->PIO_PDR = 0xffffffff; // очистка регистра
AT91C_BASE_PIOA->PIO_ASR = 0xffffffff; // выбран калнал А для генерации CLK через PA0
//AT91C_BASE_PWMC->PWMC_MR = 0x301; // 0x301
AT91C_BASE_PWMC->PWMC_CH[2].PWMC_CMR = AT91C_PWMC_CPRE_MCK; //задание частоты для счетчика. MCK= 48 МГц
AT91C_BASE_PWMC->PWMC_CH[2].PWMC_CDTYR = freq/2; //задание полупериода
AT91C_BASE_PWMC->PWMC_CH[2].PWMC_CPRDR = freq; //задание периода
AT91C_BASE_PWMC->PWMC_ENA = AT91C_PWMC_CHID2; //активация генерации CLK
}
{
unsigned long freq;
freq = BOARD_MCK/frequency;
AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_PWMC);
AT91C_BASE_PWMC->PWMC_DIS = 0xffffffff;
AT91C_BASE_PIOA->PIO_PDR = 0xffffffff; // очистка регистра
AT91C_BASE_PIOA->PIO_ASR = 0xffffffff; // выбран калнал А для генерации CLK через PA0
//AT91C_BASE_PWMC->PWMC_MR = 0x301; // 0x301
AT91C_BASE_PWMC->PWMC_CH[2].PWMC_CMR = AT91C_PWMC_CPRE_MCK; //задание частоты для счетчика. MCK= 48 МГц
AT91C_BASE_PWMC->PWMC_CH[2].PWMC_CDTYR = freq/2; //задание полупериода
AT91C_BASE_PWMC->PWMC_CH[2].PWMC_CPRDR = freq; //задание периода
AT91C_BASE_PWMC->PWMC_ENA = AT91C_PWMC_CHID2; //активация генерации CLK
}
данный код был подсмотрен гдето на форуме и немного переписан.
таким образом получаем CLK с нужной частотой по вызову функции PLL_Clock3(freq) //0 < freq <= MCK
остается реализация конвертера SPI<->USB
идея заключается в следующем:
создается 2 буфера SPI_IN[] и SPI_OUT[] с которыми и будут работать прерывания (хочется иметь возможность впихнуть эти данные еще куда-нить, например, в USART)
как правильнее реализовать прерывания таким образом, чтобы МК постоянно брал данные из SPI_OUT и писал в SPI_IN по SPI?
что и как нужно изменить в прерывании на USB, чтобы МК считывал из SPI_IN слал данные в USB и записывал в SPI_OUT данные полученные от компа?
CODE
static void UsbDataReceived(unsigned int unused,
unsigned char status,
unsigned int received,
unsigned int remaining)
{
// Check that data has been received successfully
if (status == USBD_STATUS_SUCCESS) {
// Send data through USART
while (!USART_WriteBuffer(AT91C_BASE_US0, usbBuffer, received));
AT91C_BASE_US0->US_IER = AT91C_US_TXBUFE;
// Check if bytes have been discarded
if ((received == DATABUFFERSIZE) && (remaining > 0)) {
TRACE_WARNING(
"UsbDataReceived: %u bytes discarded\n\r",
remaining);
}
}
else {
TRACE_WARNING( "UsbDataReceived: Transfer error\n\r");
}
}
static void ISR_Usart0()
{
unsigned int status = AT91C_BASE_US0->US_CSR;
unsigned short serialState;
// If USB device is not configured, do nothing
if (USBD_GetState() != USBD_STATE_CONFIGURED) {
AT91C_BASE_US0->US_IDR = 0xFFFFFFFF;
return;
}
// Buffer has been read successfully
if ((status & AT91C_US_ENDRX) != 0) {
// Disable timer
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS;
// Send buffer through the USB
while (CDCDSerialDriver_Write(usartBuffers[usartCurrentBuffer],
DATABUFFERSIZE, 0, 0) != USBD_STATUS_SUCCESS);
// Restart read on buffer
USART_ReadBuffer(AT91C_BASE_US0,
usartBuffers[usartCurrentBuffer],
DATABUFFERSIZE);
usartCurrentBuffer = 1 - usartCurrentBuffer;
// Restart timer
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
}
// Buffer has been sent
if ((status & AT91C_US_TXBUFE) != 0) {
// Restart USB read
CDCDSerialDriver_Read(usbBuffer,
DATABUFFERSIZE,
(TransferCallback) UsbDataReceived,
0);
AT91C_BASE_US0->US_IDR = AT91C_US_TXBUFE;
}
// Errors
serialState = CDCDSerialDriver_GetSerialState();
// Overrun
if ((status & AT91C_US_OVER) != 0) {
TRACE_WARNING( "ISR_Usart0: Overrun\n\r");
serialState |= CDCDSerialDriver_STATE_OVERRUN;
}
// Framing error
if ((status & AT91C_US_FRAME) != 0) {
TRACE_WARNING( "ISR_Usart0: Framing error\n\r");
serialState |= CDCDSerialDriver_STATE_FRAMING;
}
CDCDSerialDriver_SetSerialState(serialState);
}
int main()
{
TRACE_CONFIGURE(DBGU_STANDARD, 115200, BOARD_MCK);
printf("-- USB Device CDC Serial Project %s --\n\r", SOFTPACK_VERSION);
printf("-- %s\n\r", BOARD_NAME);
printf("-- Compiled: %s %s --\n\r", __DATE__, __TIME__);
// If they are present, configure Vbus & Wake-up pins
PIO_InitializeInterrupts(0);
// Configure USART
PIO_Configure(pins, PIO_LISTSIZE(pins));
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_US0;
AT91C_BASE_US0->US_IDR = 0xFFFFFFFF;
USART_Configure(AT91C_BASE_US0,
USART_MODE_ASYNCHRONOUS,
115200,
BOARD_MCK);
USART_SetTransmitterEnabled(AT91C_BASE_US0, 1);
USART_SetReceiverEnabled(AT91C_BASE_US0, 1);
AIC_ConfigureIT(AT91C_ID_US0, 0, ISR_Usart0);
AIC_EnableIT(AT91C_ID_US0);
// Configure timer 0
AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC0);
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS;
AT91C_BASE_TC0->TC_IDR = 0xFFFFFFFF;
AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV5_CLOCK
| AT91C_TC_CPCSTOP
| AT91C_TC_CPCDIS
| AT91C_TC_WAVESEL_UP_AUTO
| AT91C_TC_WAVE;
AT91C_BASE_TC0->TC_RC = 0x00FF;
AT91C_BASE_TC0->TC_IER = AT91C_TC_CPCS;
AIC_ConfigureIT(AT91C_ID_TC0, 0, ISR_Timer0);
AIC_EnableIT(AT91C_ID_TC0);
// BOT driver initialization
CDCDSerialDriver_Initialize();
// connect if needed
VBUS_CONFIGURE();
// Driver loop
while (1) {
// Device is not configured
if (USBD_GetState() < USBD_STATE_CONFIGURED) {
// Connect pull-up, wait for configuration
USBD_Connect();
while (USBD_GetState() < USBD_STATE_CONFIGURED);
// Start receiving data on the USART
usartCurrentBuffer = 0;
USART_ReadBuffer(AT91C_BASE_US0, usartBuffers[0], DATABUFFERSIZE);
USART_ReadBuffer(AT91C_BASE_US0, usartBuffers[1], DATABUFFERSIZE);
AT91C_BASE_US0->US_IER = AT91C_US_ENDRX
| AT91C_US_FRAME
| AT91C_US_OVER;
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
// Start receiving data on the USB
CDCDSerialDriver_Read(usbBuffer,
DATABUFFERSIZE,
(TransferCallback) UsbDataReceived,
0);
}
if( USBState == STATE_SUSPEND ) {
TRACE_DEBUG("suspend !\n\r");
LowPowerMode();
USBState = STATE_IDLE;
}
if( USBState == STATE_RESUME ) {
// Return in normal MODE
TRACE_DEBUG("resume !\n\r");
NormalPowerMode();
USBState = STATE_IDLE;
}
}
}
unsigned char status,
unsigned int received,
unsigned int remaining)
{
// Check that data has been received successfully
if (status == USBD_STATUS_SUCCESS) {
// Send data through USART
while (!USART_WriteBuffer(AT91C_BASE_US0, usbBuffer, received));
AT91C_BASE_US0->US_IER = AT91C_US_TXBUFE;
// Check if bytes have been discarded
if ((received == DATABUFFERSIZE) && (remaining > 0)) {
TRACE_WARNING(
"UsbDataReceived: %u bytes discarded\n\r",
remaining);
}
}
else {
TRACE_WARNING( "UsbDataReceived: Transfer error\n\r");
}
}
static void ISR_Usart0()
{
unsigned int status = AT91C_BASE_US0->US_CSR;
unsigned short serialState;
// If USB device is not configured, do nothing
if (USBD_GetState() != USBD_STATE_CONFIGURED) {
AT91C_BASE_US0->US_IDR = 0xFFFFFFFF;
return;
}
// Buffer has been read successfully
if ((status & AT91C_US_ENDRX) != 0) {
// Disable timer
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS;
// Send buffer through the USB
while (CDCDSerialDriver_Write(usartBuffers[usartCurrentBuffer],
DATABUFFERSIZE, 0, 0) != USBD_STATUS_SUCCESS);
// Restart read on buffer
USART_ReadBuffer(AT91C_BASE_US0,
usartBuffers[usartCurrentBuffer],
DATABUFFERSIZE);
usartCurrentBuffer = 1 - usartCurrentBuffer;
// Restart timer
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
}
// Buffer has been sent
if ((status & AT91C_US_TXBUFE) != 0) {
// Restart USB read
CDCDSerialDriver_Read(usbBuffer,
DATABUFFERSIZE,
(TransferCallback) UsbDataReceived,
0);
AT91C_BASE_US0->US_IDR = AT91C_US_TXBUFE;
}
// Errors
serialState = CDCDSerialDriver_GetSerialState();
// Overrun
if ((status & AT91C_US_OVER) != 0) {
TRACE_WARNING( "ISR_Usart0: Overrun\n\r");
serialState |= CDCDSerialDriver_STATE_OVERRUN;
}
// Framing error
if ((status & AT91C_US_FRAME) != 0) {
TRACE_WARNING( "ISR_Usart0: Framing error\n\r");
serialState |= CDCDSerialDriver_STATE_FRAMING;
}
CDCDSerialDriver_SetSerialState(serialState);
}
int main()
{
TRACE_CONFIGURE(DBGU_STANDARD, 115200, BOARD_MCK);
printf("-- USB Device CDC Serial Project %s --\n\r", SOFTPACK_VERSION);
printf("-- %s\n\r", BOARD_NAME);
printf("-- Compiled: %s %s --\n\r", __DATE__, __TIME__);
// If they are present, configure Vbus & Wake-up pins
PIO_InitializeInterrupts(0);
// Configure USART
PIO_Configure(pins, PIO_LISTSIZE(pins));
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_US0;
AT91C_BASE_US0->US_IDR = 0xFFFFFFFF;
USART_Configure(AT91C_BASE_US0,
USART_MODE_ASYNCHRONOUS,
115200,
BOARD_MCK);
USART_SetTransmitterEnabled(AT91C_BASE_US0, 1);
USART_SetReceiverEnabled(AT91C_BASE_US0, 1);
AIC_ConfigureIT(AT91C_ID_US0, 0, ISR_Usart0);
AIC_EnableIT(AT91C_ID_US0);
// Configure timer 0
AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC0);
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS;
AT91C_BASE_TC0->TC_IDR = 0xFFFFFFFF;
AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV5_CLOCK
| AT91C_TC_CPCSTOP
| AT91C_TC_CPCDIS
| AT91C_TC_WAVESEL_UP_AUTO
| AT91C_TC_WAVE;
AT91C_BASE_TC0->TC_RC = 0x00FF;
AT91C_BASE_TC0->TC_IER = AT91C_TC_CPCS;
AIC_ConfigureIT(AT91C_ID_TC0, 0, ISR_Timer0);
AIC_EnableIT(AT91C_ID_TC0);
// BOT driver initialization
CDCDSerialDriver_Initialize();
// connect if needed
VBUS_CONFIGURE();
// Driver loop
while (1) {
// Device is not configured
if (USBD_GetState() < USBD_STATE_CONFIGURED) {
// Connect pull-up, wait for configuration
USBD_Connect();
while (USBD_GetState() < USBD_STATE_CONFIGURED);
// Start receiving data on the USART
usartCurrentBuffer = 0;
USART_ReadBuffer(AT91C_BASE_US0, usartBuffers[0], DATABUFFERSIZE);
USART_ReadBuffer(AT91C_BASE_US0, usartBuffers[1], DATABUFFERSIZE);
AT91C_BASE_US0->US_IER = AT91C_US_ENDRX
| AT91C_US_FRAME
| AT91C_US_OVER;
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
// Start receiving data on the USB
CDCDSerialDriver_Read(usbBuffer,
DATABUFFERSIZE,
(TransferCallback) UsbDataReceived,
0);
}
if( USBState == STATE_SUSPEND ) {
TRACE_DEBUG("suspend !\n\r");
LowPowerMode();
USBState = STATE_IDLE;
}
if( USBState == STATE_RESUME ) {
// Return in normal MODE
TRACE_DEBUG("resume !\n\r");
NormalPowerMode();
USBState = STATE_IDLE;
}
}
}
т.е. должно получиться что-то типа этого:
______
| MC | < SPI_OUT < |USB
|____| > SPI_IN > |Driver
прошу помочь кто чем может. какие-то свои готовые решения (если есть), подсказка вида "где-то покопать"(только не мануалы), что-то еще дельное. может стоит взять другой пример и выкинуть из него что-либо? может еще какая идея возникнет?
ПС: я только начинаю осваивать это нелегкое дело и довольно много времени убил на реализацию данного проекта, но из дельного вышло только реализация самописанного spi (банальное вкл/выкл портов) связанного с USARTом.