|
ARM 7 (at91sam7x) Драйвер для UART |
|
|
|
Oct 10 2012, 09:40
|

Местный
  
Группа: Свой
Сообщений: 397
Регистрация: 3-12-09
Из: Россия, Москва
Пользователь №: 54 040

|
Цитата(Parkan @ Oct 10 2012, 13:29)  Ага, если PDC использует US_THR для передачи данных, тогда все понятно. Непонятно, чем PDC лучше, по сравнению с прямой записью в US_THR. На мой взгляд, позволяет избавить процессор от лишнего нахождения в режиме IRQ. Тут уже вроде говорилось об этом. А если процессор не загружен обработкой прерываний, можно и не использовать PDC, никто не заставляет  Цитата(Parkan @ Oct 10 2012, 13:29)  Разве, при возникновении прерывания TCR не всегда будет равно 0? ("ENDTX flag is set when the PERIPH_TCR register reaches zero") Только в случае, если на момент возникновения ENDTX регистр TNCR был пуст. Кстати, в даташите можно прочитать чуть далее, что "TXBUFE flag is set when both PERIPH_TCR and PERIPH_TNCR reach zero.".
|
|
|
|
|
Oct 10 2012, 09:50
|
Группа: Участник
Сообщений: 12
Регистрация: 17-04-12
Пользователь №: 71 418

|
RabidRabbit, aaarrr спасибо
|
|
|
|
|
Oct 12 2012, 13:28
|
Группа: Участник
Сообщений: 12
Регистрация: 17-04-12
Пользователь №: 71 418

|
Господа... Переписал драйвер... Теперь работает через PDC. Возникли следующие проблемы: 1) Для чтения использую буфер размером в два байта. Первый элемент помещаю в US_RPR , второй в US_RNPR. В прерывании ENDRX (RXBUFF), переписываю этот буфер в кольцевой буфер и заново инициализирую US_RPR, US_RNPR . Проблема в том, что при приеме теряются данные и выставляется флаг AT91C_US_OVRE... 2) Не знаю, насколько целесообразно использовать при передаче оба регистра US_TPR и US_TNPR 3) Если в я, в начале функции WriteBuffer запрещаю прерывание END_TX, а в конце разрешаю, то прерывание не срабатывает. Использовать функции типа __disable_irq() не хочется... Помогите решить, пожалуйста... CODE enum EUsartPDCMemoryChannel { USART_PDC_MC_0 = 0, USART_PDC_MC_1 = 1, USART_PDC_MC_COUNT }; typedef void (*FIrqHandlerCPtr)(void); struct CUsartInfo { id_type id_; // идентификатор usart AT91S_USART* usart_; // регистры usart FIrqHandlerCPtr irqHandler_; // обработчик прерываний usart size_type tprSize_; // размер передаваемого буфера в регистре-указателе памяти PDC size_type tnprSize_; // размер передаваемого буфера в регистре-указателе следующей памяти PDC token_type pdcRxBuffer_[USART_PDC_MC_COUNT]; // буффер для приема данных через PDC size_type pdcRxBufferIndex_; // индекс элемента буффера для приема данных через PDC, который в данный момент заполняется token_type txBuffer_[USART_TX_BUF_SIZE]; // буфер для передаваемой информации token_type rxBuffer_[USART_RX_BUF_SIZE]; // буфер для принимаемой информации struct kernel::ringb::CRingBuffer txRingBuffer_; // кольцевой буфер для передаваемой информации struct kernel::ringb::CRingBuffer rxRingBuffer_; // кольцевой буфер для принимаемой информации }; //--------------------------------------------------------------------------- static struct CUsartInfo usarts_[USART_ID_COUNT]; static status_type usartStatuses_[USART_ID_COUNT]; static const Pin usartPins_[] = { BOARD_PIN_USART_RXD, BOARD_PIN_USART_TXD, BOARD_PIN_USART_CTS, BOARD_PIN_USART_RTS }; //--------------------------------------------------------------------------- volatile unsigned int cc = 0; // обработчик прерывания usart inline void doIrqHandler(handle_type handle) { CUsartInfo* const info = &usarts_[handle]; unsigned int status = info->usart_->US_CSR; status &= info->usart_->US_IMR; // если произошла ошибка if ((status & USART_ST_HARDWARE_ERROR)) { usartStatuses_[handle] |= (status & USART_ST_HARDWARE_ERROR); info->usart_->US_CR = AT91C_US_RSTSTA; } // содержимое регистра US_TCR достигло 0 // (т.е. данные, содержащиеся в регистре-указателе памяти отправились) if (AT91C_US_ENDTX == (status & AT91C_US_ENDTX)) { // стираем из кольцевого буфера, переданный через PDC блок данных SANITY_CHECK(info->tprSize_ && info->tprSize_ <= kernel::ringb::SizeForRead(&info->txRingBuffer_));
kernel::ringb::Erase(&info->txRingBuffer_, info->tprSize_); info->tprSize_ = 0; // если, в регистре-указателе следующей памяти есть данные // if (info->usart_->US_TCR) // { // // то теперь они в регистре-указателе памяти и размер этих данных info->tnprSize_ // SANITY_CHECK(info->tnprSize_); // info->tprSize_ = info->tnprSize_; // info->tnprSize_ = 0; // } // если в кольцевом буфере есть не помещенные в PDC данные // if (info->tprSize_ < kernel::ringb::SizeForRead(&info->txRingBuffer_)) if (!kernel::ringb::IsEmpty(&info->txRingBuffer_)) { // SANITY_CHECK(info->tprSize_); // получаем из кольцевого буфера указатель на непрерывный блок данных token_type* txBuf = 0; kernel::ringb::size_type txBufSize = 0; kernel::ringb::ReadPersistentDataBlock(&info->txRingBuffer_, &txBuf, txBufSize, info->tprSize_); SANITY_CHECK(txBufSize);
// if (info->usart_->US_TCR) // { // // и помещаем этот блок-данных в регистр-указатель следующей памяти, // // так как регистр-указатель памяти уже содержит данные // info->tnprSize_ = txBufSize; // info->usart_->US_TNPR = (unsigned int)txBuf; // info->usart_->US_TNCR = txBufSize; // } // else // { info->tprSize_ = txBufSize; info->usart_->US_TPR = (unsigned int)txBuf; info->usart_->US_TCR = txBufSize; // } } // в случае, запрещаем прерывание AT91C_US_ENDTX //if (!info->tprSize_) else { info->usart_->US_IDR = AT91C_US_ENDTX; } } if (AT91C_US_RXBUFF == (status & AT91C_US_RXBUFF)) { // if (0 == info->usart_->US_RCR) // { if (USART_PDC_MC_COUNT > kernel::ringb::SizeForWrite(&info->rxRingBuffer_)) { usartStatuses_[handle] |= USART_ST_RX_BUF_OVERFLOW; } else { kernel::ringb::WriteBuffer(&info->rxRingBuffer_, info->pdcRxBuffer_, 2); // info->pdcRxBufferIndex_ = (info->pdcRxBufferIndex_ + 1) & 0x1; // kernel::ringb::Write(&info->rxRingBuffer_, info->pdcRxBuffer_[info->pdcRxBufferIndex_]); // kernel::ringb::Write(&info->rxRingBuffer_, info->pdcRxBuffer_[info->pdcRxBufferIndex_]); // info->pdcRxBufferIndex_ = (info->pdcRxBufferIndex_ + 1) & 0x1; // kernel::ringb::Write(&info->rxRingBuffer_, info->pdcRxBuffer_[info->pdcRxBufferIndex_]); } info->usart_->US_RPR = (unsigned int)&info->pdcRxBuffer_[tools::USART_PDC_MC_0]; info->usart_->US_RCR = 1; info->usart_->US_RNPR = (unsigned int)&info->pdcRxBuffer_[tools::USART_PDC_MC_1]; info->usart_->US_RNCR = 1; info->pdcRxBufferIndex_ = 0; // } // else // { // if (kernel::ringb::IsFull(&info->rxRingBuffer_)) // { // usartStatuses_[handle] |= USART_ST_RX_BUF_OVERFLOW; // } // else // { // kernel::ringb::Write(&info->rxRingBuffer_, info->pdcRxBuffer_[info->pdcRxBufferIndex_]); // } // info->usart_->US_RNPR = (unsigned int)&info->pdcRxBuffer_[info->pdcRxBufferIndex_]; // info->usart_->US_RNCR = 1; // info->pdcRxBufferIndex_ = (info->pdcRxBufferIndex_ + 1) & 0x1; // } } } // обработчик прерываний для USART0 (AT91C_ID_US0) void IrqHandler0(void) { doIrqHandler(USART_ID_0); } // обработчик прерываний для USART1 (AT91C_ID_US1) void IrqHandler1(void) { doIrqHandler(USART_ID_1); } } //---------------------------------------------------------------------------
void Init(void) { PIO_Configure(tools::usartPins_, PIO_LISTSIZE(tools::usartPins_));
tools::usarts_[USART_ID_0].usart_ = AT91C_BASE_US0; tools::usarts_[USART_ID_0].id_ = AT91C_ID_US0; tools::usarts_[USART_ID_0].irqHandler_ = tools::IrqHandler0;
tools::usarts_[USART_ID_1].usart_ = AT91C_BASE_US1; tools::usarts_[USART_ID_1].id_ = AT91C_ID_US1; tools::usarts_[USART_ID_1].irqHandler_ = tools::IrqHandler1; for (token_type i(0); i < USART_ID_COUNT; ++i) { tools::usarts_[i].tprSize_ = 0; tools::usarts_[i].tnprSize_ = 0; tools::usarts_[i].pdcRxBufferIndex_ = 0;
kernel::ringb::Init(&tools::usarts_[i].txRingBuffer_, tools::usarts_[i].txBuffer_, USART_TX_BUF_SIZE); kernel::ringb::Init(&tools::usarts_[i].rxRingBuffer_, tools::usarts_[i].rxBuffer_, USART_RX_BUF_SIZE);
memset(tools::usarts_[i].pdcRxBuffer_, 0, tools::USART_PDC_MC_COUNT);
tools::usartStatuses_[i] = USART_ST_OK; } } //---------------------------------------------------------------------------
handle_type Open(const id_type usartId, const mode_type mode, const baudrate_type baudRate, const sync_frequency_type masterClockFrequency) { SANITY_CHECK(usartId < USART_ID_COUNT && baudRate && ((mode & AT91C_US_USMODE_ISO7816_0) != AT91C_US_USMODE_ISO7816_0) && ((mode & AT91C_US_USMODE_ISO7816_1) != AT91C_US_USMODE_ISO7816_1) && ((mode & AT91C_US_USMODE_IRDA) != AT91C_US_USMODE_IRDA)); tools::CUsartInfo* const info = &tools::usarts_[usartId];
kernel::ringb::Clear(&info->txRingBuffer_); kernel::ringb::Clear(&info->rxRingBuffer_); info->tprSize_ = 0; info->tnprSize_ = 0; info->pdcRxBufferIndex_ = 0;
PMC_EnablePeripheral(info->id_);
info->usart_->US_CR = AT91C_US_RSTRX | AT91C_US_RSTTX | AT91C_US_RSTSTA; info->usart_->US_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS; info->usart_->US_MR = mode; if ((info->usart_->US_MR & AT91C_US_SYNC)) { info->usart_->US_BRGR = masterClockFrequency / baudRate; } else { const token_type freqSemplingMode = (info->usart_->US_MR & AT91C_US_OVER) ? 1 : 2; info->usart_->US_BRGR = masterClockFrequency / (8 * freqSemplingMode * baudRate); }
info->usart_->US_RPR = (unsigned int)&info->pdcRxBuffer_[tools::USART_PDC_MC_0]; info->usart_->US_RCR = 1; info->usart_->US_RNPR = (unsigned int)&info->pdcRxBuffer_[tools::USART_PDC_MC_1]; info->usart_->US_RNCR = 1; info->usart_->US_IER = AT91C_US_RXBUFF | AT91C_US_OVRE | AT91C_US_FRAME | AT91C_US_PARE; info->usart_->US_CR = AT91C_US_RXEN | AT91C_US_TXEN ; info->usart_->US_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN;
tools::usartStatuses_[usartId] = USART_ST_OK;
IRQ_ConfigureIT(info->id_, 0, info->irqHandler_); IRQ_EnableIT(info->id_); return usartId; } //--------------------------------------------------------------------------- bool IsError(const handle_type handle) { SANITY_CHECK(!IsInvalidHandle(handle)); return tools::usartStatuses_[handle] != USART_ST_OK; } //--------------------------------------------------------------------------- status_type GetStatus(const handle_type handle) { SANITY_CHECK(!IsInvalidHandle(handle)); return tools::usartStatuses_[handle]; } //--------------------------------------------------------------------------- bool IsInvalidHandle(const handle_type handle) { return handle >= USART_ID_COUNT; } //--------------------------------------------------------------------------- bool IsDataAvailableForReading(const handle_type handle) { SANITY_CHECK(!IsInvalidHandle(handle)); return !kernel::ringb::IsEmpty(&tools::usarts_[handle].rxRingBuffer_); } //--------------------------------------------------------------------------- void WaitForData(const handle_type handle) { SANITY_CHECK(!IsInvalidHandle(handle));
while (!IsDataAvailableForReading(handle)) { continue; } } //--------------------------------------------------------------------------- size_type Read(const handle_type handle, token_type* const data) { return ReadBuffer(handle, data, 1); } //--------------------------------------------------------------------------- size_type ReadBuffer(const handle_type handle, token_type* const buffer, const size_type bufferSize) { SANITY_CHECK(!IsInvalidHandle(handle) && buffer);
tools::CUsartInfo* const info = &tools::usarts_[handle]; if (!kernel::ringb::IsEmpty(&info->rxRingBuffer_)) { return kernel::ringb::ReadBuffer(&info->rxRingBuffer_, buffer, min(bufferSize, kernel::ringb::SizeForRead(&info->rxRingBuffer_))); } return 0; } //--------------------------------------------------------------------------- void Write(const handle_type handle, const token_type data) { WriteBuffer(handle, &data, 1); } //--------------------------------------------------------------------------- void WriteBuffer(const handle_type handle, const token_type* const buffer, const size_type bufferSize) { SANITY_CHECK(!IsInvalidHandle(handle));
if (!bufferSize) { return; } tools::CUsartInfo* const info = &tools::usarts_[handle]; if (kernel::ringb::IsFull(&info->txRingBuffer_) || bufferSize > kernel::ringb::SizeForWrite(&info->txRingBuffer_)) { tools::usartStatuses_[handle] |= USART_ST_TX_BUF_OVERFLOW; return; } kernel::ringb::WriteBuffer(&info->txRingBuffer_, buffer, bufferSize); // в регистре-указателе памяти нет данных (а в регистре-указателе следующей памяти и подавно) if (!info->usart_->US_TCR) { token_type* txBuf = 0; kernel::ringb::size_type txBufSize = 0; kernel::ringb::ReadPersistentDataBlock(&info->txRingBuffer_, &txBuf, txBufSize, 0); SANITY_CHECK(txBufSize); info->tprSize_ = txBufSize; info->usart_->US_TPR = (unsigned int)txBuf; info->usart_->US_TCR = txBufSize;
info->usart_->US_IER = AT91C_US_ENDTX; } } //---------------------------------------------------------------------------
|
|
|
|
|
Oct 13 2012, 13:01
|
Группа: Участник
Сообщений: 12
Регистрация: 17-04-12
Пользователь №: 71 418

|
Цитата(aaarrr @ Oct 12 2012, 18:34)  1. Сделайте буферы большого размера и используйте помимо ENDRX прерывание TIMEOUT. 2. Можно и не использовать, если выходной поток допускает разрывы. 3. А зачем запрещать ENDTX где-то еще, кроме самого прерывания? Здравствуйте. 2)Я не понял, что значит "выходной поток допускает разрывы"... Вы имели ввиду, что без использования TNPR памяти будет переполнение буфера? 3) Если использовать регистр-указатель следующей памяти, то необходимо хранить в переменных текущие размеры передаваемых в TPR и в TNPR данных. Эти переменные будут изменяться в функции WriteBuffer и в обработчике прерываний, что может привести к некорректному поведению... Т.е. с использованием TNPR я мыслил следующий алгоритм функции WriteBuffer 1) я записываю переданный буфер в кольцевой буфер. 2) далее проверяю TCR на 0 и TNCR на 0 (чтобы выполнялось одновременно), если верно, то помещаю в TPR блок данных из кольцевого буфера и сохраняю размер этого блока в переменной, скажем tprSize_ 3) иначе проверяю TNCR на 0, если верно то помещаю в TNPR блок данных из кольцевого буфера и сохраняю размер этого блока в переменной, скажем tnpSize_. В момент когда я вошел в условие, но еще не поместил в TNPR данные происходит прерывание END_TX, в котором я также проверяю свободность кольцевого буфера и помещаю из него данные в PDC. В этом случае и возникает некорректная ситуация, заключающаяся в том, что я попытаюсь два раза одни и те же данные передать, сначала взяв их из буфера в прерывании, а затем взяв их из буфера получившей управление после прерывания функции WriteBuffer.
|
|
|
|
|
Oct 13 2012, 14:13
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(Parkan @ Oct 13 2012, 17:01)  2)Я не понял, что значит "выходной поток допускает разрывы"... Вы имели ввиду, что без использования TNPR памяти будет переполнение буфера? Возможен простой передатчика, если процессор не может уйти в прерывание достаточно оперативно. Цитата(Parkan @ Oct 13 2012, 17:01)  3) Если использовать регистр-указатель следующей памяти, то необходимо хранить в переменных текущие размеры передаваемых в TPR и в TNPR данных. Эти переменные будут изменяться в функции WriteBuffer и в обработчике прерываний, что может привести к некорректному поведению... Не нужно в функции WriteBuffer трогать регистры PDC: 1. Положили данные в FIFO 2. Разрешили прерывание ENDTX Все остальное делается уже в обработчике прерывания.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|