BSY показывает что передача физически еще идет..
а TXE показывает что можно следующий байт класть в SPI_DR
просто в STM32 данные уходят не из SPI_DR !! цепочка следующая:
1) записываем данные в SPI_DR
2) данные из SPI_DR перезаписываются в сдвиговый регистр, и уже из него уходят на MOSI... здесь важно понять что сдвиговый регистр - это еще один регистр !!! то есть при записи байта передача идет по цепочке SPI_DR -> Сдвиговый регистр -> MOSI
3) когда данные ушли из SPI_DR в сдвиговый регистр - тогда выставляется флаг TXE !! то есть байт еще передается (фактически начал передаваться из сдвигового регистра) а флаг TXE сбрасывается ! - он показывает что в SPI_DR можно загрузить следующий байт данных, КОТОРЫЙ БУДЕТ ЖДАТЬ (!) пока не уйдет текущий байт (который уже в сдвиговом уходит не спеша)...
В этом и кроются основные грабли при работе с дисплеями у которых есть вход Data/Command !
если вы отправили в SPI_DR код команды, предварительно установив DC=0
- Установка DC=0;
- отправка в DR=код команды
- Здесь проверка флага TXE
То вы проверку TXE пройдете сразу !!! так как после записи в SPI_DR байт сразу уйдет в сдвиговый !!!
если на этот момент не обратить внимание и следующим байтом послать данные
- Установка DC=1
- отправка в DR=данные команды
- Здесь проверка флага TXE
то будет ошибка !!! потому что дисплей еще не получил код команды (реально по MOSI пройдет максимум пара бит) - а вы меняете состояние линии DC !!!
в вашем драйвере вы интуитивно решили это введением задержки (фактически перед сменой DC0 в DC1). и это до определенных скоростей будет работать...
Но как видите - это не правильно !
Правильно перед сменой DC проверять флаг BSY !! так как этот флаг сбросится только тогда когда оба регистра (и SPI_DR и сдвиговый) будут пустыми !! то есть все будет отправлено и DC можно будет менять безопасно...
Соответственно TXE - это флаг того что данные можно пихать в SPI_DR - поэтому его нужно проверять перед записью в SPI_DR, то есть передача команды будет выглядеть вот так:
- Установка DC=0;
- Здесь проверка флага TXE
- отправка в DR=код команды
а передача данных после нее так:
- Здесь проверка флага BSY
- Установка DC=1
- Здесь проверка флага TXE
- отправка в DR=данные команды
И не нужно никаких задержек ! вне зависимости от скорости SPI от минимума до максимума все будет работать !!
Само собой если мы передаем однородные данные (только команды или только данные) - то BSY проверять не нужно (мы же не собираемся дергать DC) и проверяем только TXE !!!
Выигрыш в том что пока передаются данные - контроллер будет занят полезным - например готовить следующие байты к отправке..
Опять таки - если после данных мы решили опять передать команды (как например в том же ST7735 или ILI9341C) - то перед командой с ее сменой DC - нужно проверить флаг BSY !!
Я у себя в программе просто сделал три подпрограммы:
- передача байта (не трогая DC) Но с ожиданием TXE
- передача байта с DC0 и ожиданием BSY - это для команд
- передача байта с DC1 и ожиданием BSY - это для данных
Соответственно для инициализации использую только 2 и 3 процедуры (они передают с нужным DC и дожидаются по BSY фактического окончания передачи)
а вот при передаче данных при работе с дисплеем - использую первую ! (ну конечно иногда перед ней вызываю вторую для передачи команды COL/ROW позиционирования или доступа к GRAM)...
Вот например для ILI9341C (еще я перехожу в 16ти битные посылки при передачи данных - по скорости сравниваюсь с DMA передачей !!)
Код: Выделить всё
// передача данных на дисплей
void SPI2_SendByte(uint8_t sendData)
{
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI2, sendData);
}
// отправка команды на дисплей
void ili9341c_CMD(uint8_t sendData)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_12); // DC=0;
SPI2_SendByte(sendData);
while (SPI_I2S_GetFlagStatus(SPI2,SPI_FLAG_BSY)!=RESET);
}
// отправка данных на дисплей
void ili9341c_DATA(uint8_t sendData)
{
GPIO_SetBits(GPIOB, GPIO_Pin_12); // DC=1;
SPI2_SendByte(sendData);
while (SPI_I2S_GetFlagStatus(SPI2,SPI_FLAG_BSY)!=RESET);
}
// определение области для вывода
void ili9341c_SetWindow(uint16_t ystart, uint16_t xstart, uint16_t yend, uint16_t xend)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_12); // DC=0;
SPI2_SendByte(LCD_PAGE_ADDR);
while (SPI_I2S_GetFlagStatus(SPI2,SPI_FLAG_BSY)!=RESET);
GPIO_SetBits(GPIOB, GPIO_Pin_12); // DC=1;
SPI2_SendByte(xstart>>8);
SPI2_SendByte(xstart&0xFF);
SPI2_SendByte(xend>>8);
SPI2_SendByte(xend&0xFF);
while (SPI_I2S_GetFlagStatus(SPI2,SPI_FLAG_BSY)!=RESET);
GPIO_ResetBits(GPIOB, GPIO_Pin_12); // DC=0;
SPI2_SendByte(LCD_COLUMN_ADDR);
while (SPI_I2S_GetFlagStatus(SPI2,SPI_FLAG_BSY)!=RESET);
GPIO_SetBits(GPIOB, GPIO_Pin_12); // DC=1;
SPI2_SendByte(ystart>>8);
SPI2_SendByte(ystart&0xFF);
SPI2_SendByte(yend>>8);
SPI2_SendByte(yend&0xFF);
while (SPI_I2S_GetFlagStatus(SPI2,SPI_FLAG_BSY)!=RESET);
}
// заполнение цветом прямоугольной области экрана
void ili9341c_FillRect(uint16_t ystart, uint16_t xstart, uint16_t ystop, uint16_t xstop, uint16_t color)
{
uint32_t n;
GPIO_ResetBits(GPIOB, GPIO_Pin_11); // CS=0;
ili9341c_SetWindow(ystart, xstart, ystop, xstop); // определим окно для заполнения
GPIO_ResetBits(GPIOB, GPIO_Pin_12); // DC=0;
SPI2_SendByte(LCD_GRAM);
while (SPI_I2S_GetFlagStatus(SPI2,SPI_FLAG_BSY)!=RESET);
GPIO_SetBits(GPIOB, GPIO_Pin_12); // DC=1;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;
SPI_Init(SPI2, &SPI_InitStructure);
for(n=0;n<(xstop-xstart+1)*(ystop-ystart+1);n++) { // заполняем цветом область 16ти битными посылками
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI2, color);
}
while (SPI_I2S_GetFlagStatus(SPI2,SPI_FLAG_BSY)!=RESET);
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_Init(SPI2, &SPI_InitStructure);
GPIO_SetBits(GPIOB, GPIO_Pin_11); // CS=1;
}
p.s. говоря байты - я подразумеваю что это могут быть и байты и слова в 2 байта
![Smile :-)](./images/smilies/icon_e_smile.gif)
))
Дополнительно, само собой что CS тоже нужно менять после проверки BSY !! иначе у дисплеев тоже сносит крышу...
При работе с SD картой - все проще - там нет DC
![Smile :-)](./images/smilies/icon_e_smile.gif)
поэтому BSY можно не проверять, а работать одним только TXE, хотя это не верно ! например перед отключением CS карты желательно все таки проверить BSY, а то еще глюканет - но там спасает достаточно большое количество команд между отправкой последнего байта и отключением CS и как правило ни на что не влияет. Опять таки если вам нужно будет максимальное быстродействие от SD карты - то придется и там озадачиться...