Я пишу драйвер SPI SD-карты для процессора. Я знаю, что драйвер SPI хорош, потому что я также использую его для ЖК-дисплея и до сих пор весело отправлял команды на карту. У меня проблемы с одной конкретной транзакцией, которая ничем не отличается от любой другой, которую я вижу.
После записи данных на шину SPI я жду обратных вызовов прерывания, чтобы сказать, что прием и передача завершены. Существует два обратных вызова, которые устанавливают следующие логические флаги в struct
к истине:
m_transaction_status.sdts_rx_complete
m_transaction_status.sdts_tx_complete
В обычном потоке программы я жду, пока будут установлены эти флаги, прежде чем продолжить:
while ((m_transaction_status.sdts_rx_complete == false) //
|| (m_transaction_status.sdts_tx_complete == false))
{
// Check whether we've had an error.
if (m_transaction_status.sdts_error == true)
{
LOG_ERROR(SD::MODULE,
"transfer_data: Transaction error.");
transfer_status = false;
break;
}
}
Для проблемной транзакции я установил контрольную точку в обратном вызове и вижу, что оба флага установлены, но когда я возвращаюсь к нормальному потоку, переменные внезапно становятся недоступными и кажутся false
так как я тогда навсегда застрял в петле. Этот цикл используется для любой другой транзакции без проблем.
Есть ли какая-то причина, по которой я мог бы выяснить, почему эти переменные устанавливаются, но затем (очевидно) очищаются или освобождаются или что-то еще не в порядке?
РЕДАКТИРОВАТЬ:
Прерывание действительно является микро (Renesas RL78, если быть точным).
Объявление структуры:
/**
* The state of an SD SPI transaction.
*/
struct SDTransactionStatus
{
/** Transaction error flag. */
bool sdts_error;
/** Transmission completion flag. */
bool sdts_tx_complete;
/** Reception completion flag. */
bool sdts_rx_complete;
};
struct
реализован как частный член моего SD
учебный класс. Флаги устанавливаются через функцию простого установщика в обратном вызове.
Я не знаю ни вашего конкретного микроконтроллера, ни компилятора C ++, который вы должны использовать с ним, но это не должно ничего менять в том, что я собираюсь сказать.
Я считаю, что проблема в том, что в вашем цикле вы никогда не меняете значение своих флагов, вы просто читаете их, поэтому компилятор может оптимизировать вещи и читать ваши переменные только один раз за пределами цикла (т.е. кэшировать их), потому что это не Не знаю, они могут измениться. Что-то вроде:
const bool complete = !m_transaction_status.sdts_rx_complete
|| !m_transaction_status.sdts_tx_complete;
const bool error = m_transaction_status.sdts_error;
while (complete) {
if (error) {
//...
}
}
Вы понимаете, почему это не может дать хорошие результаты. Исправление заключается в том, чтобы сообщить компилятору, что ваши переменные Можно изменить «за его спиной», и что на самом деле нужно их перечитать каждый раз, когда вы получаете к ним доступ в своем коде.
Так как вы не многопоточны или что-то еще, вы можете сделать это, просто добавив volatile
квалификатор ваших переменных в вашем объявлении вашего struct
:
struct SDTransactionStatus
{
volatile bool sdts_error;
volatile bool sdts_tx_complete;
volatile bool sdts_rx_complete;
};
Теперь, почему вы не наткнулись на эту проблему раньше, является загадкой, но вы должны помнить, что «компилятор может оптимизировать«не значит, что это всегда должен. Как дикая догадка, я бы сказал, что другие места, которые вы использовали в этом цикле, не поддаются такой оптимизации, в то время как место, где у вас есть проблема, делает.
Конечно, я могу ошибаться во всем этом, но с таким небольшим количеством информации трудно сказать, и в любом случае эта проблема оптимизации / нестабильности все еще существует в вашем коде, даже если это не является причиной вашей текущей проблемы, поэтому вам все равно придется ее устранять, потому что даже если ваша текущая проблема не связана, то, что я описал, может произойти в любое время.
Редактировать: О, и вы, вероятно, должны рассмотреть свой все кодовая база, чтобы гарантировать, что это не случится где-то еще.
Других решений пока нет …