Форум программистов, компьютерный форум, киберфорум
Микроконтроллеры ARM, Cortex, STM32
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.84/25: Рейтинг темы: голосов - 25, средняя оценка - 4.84
1 / 1 / 0
Регистрация: 22.06.2016
Сообщений: 96

Вывод информации на TFT через SPI

04.12.2018, 08:06. Показов 5871. Ответов 55

Студворк — интернет-сервис помощи студентам
Всем доброго дня. Помогите с выводом информации на TFT-экран (в моем случае на ILI9341 1,8 дюйма). Пример с ногодрыгом нашел, освоил, вывод букв, строк получается. Теперь хотелось бы вывести информацию через SPI2 (SPI1 у меня уже занят под потенциометры АЦП), да и что-то мне подсказывает, что аппаратная реализация будет правильнее.

В коде для отслеживания прописал синий светодиод плюс осциком на выводе PB15 (SPI2_MOSI) вижу сигналы. После инициализации экран немного изменяет цвет и ничего не происходит, хотя должен выводиться небольшой красный квадрат. Может быть в последовательностях CS, DC и RESET ошибся? Либо в SPI2 отправка данных неправильная ...
Инициализация TFT точно рабочая, так как в режиме ногодрыга она актуальна.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
#include "stm32f10x.h"
 
/*PC0 - CS (3), PB10 - RESET (4), PB11 - DC (5), PC1 - LED (8), PB13 - SPI2_SCK (7), PB14 - SPI2_MISO (9), PB15 - SPI2_MOSI(6)----*/
  
// Colors
#define RED     0xf800
#define GREEN       0x07e0
#define BLUE        0x001f
#define BLACK       0x0000
#define YELLOW      0xffe0
#define WHITE       0xffff
#define CYAN        0x07ff
#define BRIGHT_RED  0xf810
#define GRAY1       0x8410
#define GRAY2       0x4208
 
#define CS_LOW           GPIOC->BSRR |= GPIO_BSRR_BR0
#define CS_HIGH          GPIOC->BSRR |= GPIO_BSRR_BS0
 
#define DC_LOW           GPIOB->BSRR |= GPIO_BSRR_BR11
#define DC_HIGH          GPIOB->BSRR |= GPIO_BSRR_BS11
 
#define RESET_LOW        GPIOB->BSRR |= GPIO_BSRR_BR10
#define RESET_HIGH       GPIOB->BSRR |= GPIO_BSRR_BS10
 
 
/*-----------------------------------------------------Тактирование-----------------------------------*/
void RCC_Init () {
  
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);                      // Enable HSE
    while (!(RCC->CR & RCC_CR_HSERDY));                 // Ready start HSE      
    
    FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;          // Cloclk Flash memory
    
    RCC->CFGR |= RCC_CFGR_HPRE_DIV1;                    // AHB = SYSCLK/1
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV1;                   // APB1 = HCLK/1
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;                   // APB2 = HCLK/1
    
    RCC->CFGR &= ~RCC_CFGR_PLLMULL;                             // clear PLLMULL bits
    RCC->CFGR &= ~RCC_CFGR_PLLSRC;                      // clearn PLLSRC bits
    RCC->CFGR &= ~RCC_CFGR_PLLXTPRE;                    // clearn PLLXTPRE bits
    
    RCC->CFGR |= RCC_CFGR_PLLSRC_PREDIV1;                   // source HSE
    RCC->CFGR |= RCC_CFGR_PLLXTPRE_PREDIV1_Div2;                // source HSE/2 = 4 MHz
    RCC->CFGR |= RCC_CFGR_PLLMULL6;                     // PLL x6: clock = 4 MHz * 6 = 24 MHz
    
    RCC->CR |= RCC_CR_PLLON;                                    // enable PLL
    while((RCC->CR & RCC_CR_PLLRDY) == 0) {}                    // wait till PLL is ready
    
    RCC->CFGR &= ~RCC_CFGR_SW;                                  // clear SW bits
        RCC->CFGR |= RCC_CFGR_SW_PLL;                                   // select source SYSCLK = PLL
    while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_1) {}          // wait till PLL is used
        
        //Тактирование периферии
  
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN;
 
RCC->APB1ENR |= RCC_APB1ENR_SPI2EN; // Тактируем SPI2
 
}
 
 
 
 
/*----------------------------Инициализация ног для светодиодов на отладочной плате---------------------------------*/
 
void GPIO_LEDInit () {
  
   GPIOC->CRH |= GPIO_CRH_MODE8; GPIOC->CRH &= ~GPIO_CRH_CNF8;
   GPIOC->CRH |= GPIO_CRH_MODE9; GPIOC->CRH &= ~GPIO_CRH_CNF9; //Выводы синего и зеленого светодиодов на отладочной плате
  
}
 
 
void Blue_On (int sw) {
  
  if (sw) GPIOC->BSRR |= GPIO_BSRR_BS8;
  else GPIOC->BSRR |= GPIO_BSRR_BR8;
  
};
 
 
 
/*-----------------------------Инициализация ножек SPI2 и TFT---------------------------------------*/
void GPIO_TFTlegs_Init () {
  /*----Смотреть стр.107 RM0041-----*/
/*PC0 - CS (3), PB10 - RESET (4), PB11 - DC (5), PC1 - LED (8), PB13 - SPI2_SCK (7), PB14 - SPI2_MISO (9), PB15 - SPI2_MOSI(6)----*/
  
  GPIOB->CRH &= ~GPIO_CRH_CNF10 & ~GPIO_CRH_CNF11;
  GPIOB->CRH |= GPIO_CRH_MODE10 | GPIO_CRH_MODE11;
  
  GPIOC->CRL |= GPIO_CRL_MODE0; GPIOC->CRL &= ~GPIO_CRL_CNF0;
  GPIOC->CRL |= GPIO_CRL_MODE1; GPIOC->CRL &= ~GPIO_CRL_CNF1; 
  
  //вывод SCK: выход двухтактный, альтернативная функция, 50MHz
GPIOB->CRH   |=  GPIO_CRH_MODE13;    //
GPIOB->CRH   &= ~GPIO_CRH_CNF13;     //
GPIOB->CRH   |=  GPIO_CRH_CNF13_1;   //
 
//вывод MISO: вход цифровой с подтягивающим резистором, подтяжка к плюсу
GPIOB->CRH   &= ~GPIO_CRH_MODE14;    //
GPIOB->CRH   &= ~GPIO_CRH_CNF14;     //
GPIOA->CRH   |=  GPIO_CRH_CNF14_1;   //
GPIOB->BSRR   =  GPIO_BSRR_BS14;     //
 
//вывод MOSI: выход двухтактный, альтернативная функция, 50MHz
GPIOB->CRH   |=  GPIO_CRH_MODE15;    //
GPIOB->CRH   &= ~GPIO_CRH_CNF15;     //
GPIOB->CRH   |=  GPIO_CRH_CNF15_1;   //
     
}
 
 
void Init_SPI2() {
  
  SPI2->CR1 = 0x0000;
  SPI2->CR2 = 0x0000;
  SPI2->CR1 |= SPI_CR1_BR; //fpclk/256 = 24 000 000/256 = 93750
  SPI2->CR1 &= ~SPI_CR1_CPOL;             //Polarity cls signal CPOL = 0;
  SPI2->CR1 &= ~SPI_CR1_CPHA;             //Phase cls signal    CPHA = 0;
  SPI2->CR1 &= ~SPI_CR1_DFF;               //8 bit data
  SPI2->CR1 &= ~SPI_CR1_LSBFIRST; // Ногами вперед
  SPI2->CR1    |= SPI_CR1_SSI;        //обеспечить высокий уровень программного NSS
  SPI2->CR1    |= SPI_CR1_SSM;        //разрешить программное формирование NSS
  SPI2->CR1    |= SPI_CR1_MSTR;       //контроллер должен быть мастером 
  SPI2->CR1    |= SPI_CR1_SPE;        //разрешить работу модуля SPI2
  
}
 
 
 
/*-----------------------------------------------------Отправка байта данных в TFT-----------------------------------------*/
void TFT_SEND_BYTE(int data) {while(!(SPI2->SR & SPI_SR_TXE)); SPI2->DR = data;}
 
/*-----------------------------------------------------Отправка команды в TFT----------------------------------------------*/
void TFT_SEND_CMD(int cmd) {while(SPI2->SR & SPI_SR_BSY); CS_LOW; TFT_SEND_BYTE(cmd);}
 
/*-----------------------------------------------------Отправка данных в TFT-----------------------------------------------*/
void TFT_SEND_DATA(int data) {while(SPI2->SR & SPI_SR_BSY); DC_HIGH; TFT_SEND_BYTE(data);}
 
/*----------------------------------------------------Отправка слова в TFT-------------------------------------------------*/
void TFT_SEND_WORD(int data) {while(SPI2->SR & SPI_SR_BSY); DC_HIGH; TFT_SEND_BYTE(data >> 8); TFT_SEND_BYTE(data & 0x00FF);}
 
 
 
/*-----------------------------------------------------Инициализируем TFT--------------------------------------------------*/
void TFT_INIT()
 
{
  
  GPIO_TFTlegs_Init ();
  Init_SPI2 ();
 
  CS_HIGH;
  RESET_HIGH;
  TFT_SEND_CMD(0x01);
  for (int i=0;i<1000000;i++);
  
   
  //Поехали настраивать  TFT!!!
 TFT_SEND_CMD(0xCB);
    TFT_SEND_DATA(0x39);
    TFT_SEND_DATA(0x2C);
    TFT_SEND_DATA(0x00);
    TFT_SEND_DATA(0x34);
    TFT_SEND_DATA(0x02);
 
  TFT_SEND_CMD(0xCF);
    TFT_SEND_DATA(0x00);
    TFT_SEND_DATA(0xC1);
    TFT_SEND_DATA(0x30);
 
  TFT_SEND_CMD(0xE8);
    TFT_SEND_DATA(0x85);
    TFT_SEND_DATA(0x00);
    TFT_SEND_DATA(0x78);
 
  TFT_SEND_CMD(0xEA);
    TFT_SEND_DATA(0x00);
    TFT_SEND_DATA(0x00);
 
  TFT_SEND_CMD(0xED);
    TFT_SEND_DATA(0x64);
    TFT_SEND_DATA(0x03);
    TFT_SEND_DATA(0x12);
    TFT_SEND_DATA(0x81);
 
  TFT_SEND_CMD(0xF7);
    TFT_SEND_DATA(0x20);
 
  TFT_SEND_CMD(0xC0);       //Power control
    TFT_SEND_DATA(0x23);    //VRH[5:0]
 
  TFT_SEND_CMD(0xC1);       //Power control
    TFT_SEND_DATA(0x10);    //SAP[2:0];BT[3:0]
 
  TFT_SEND_CMD(0xC5);       //VCM control
    TFT_SEND_DATA(0x3E);    //Contrast
    TFT_SEND_DATA(0x28);
 
  TFT_SEND_CMD(0xC7);       //VCM control2
    TFT_SEND_DATA(0x86);     //--
 
  TFT_SEND_CMD(0x36);       // Memory Access Control
    TFT_SEND_DATA(0x48);    //C8       //48 68з»”ж*§зќ†//28 E8 е¦ЇоЃ„зќ†
 
  TFT_SEND_CMD(0x3A);
    TFT_SEND_DATA(0x55);
 
  TFT_SEND_CMD(0xB1);
    TFT_SEND_DATA(0x00);
    TFT_SEND_DATA(0x18);
 
  TFT_SEND_CMD(0xB6);       // Display Function Control
    TFT_SEND_DATA(0x08);
    TFT_SEND_DATA(0x82);
    TFT_SEND_DATA(0x27);
 
  TFT_SEND_CMD(0xF2);       // 3Gamma Function Disable
    TFT_SEND_DATA(0x00);
 
  TFT_SEND_CMD(0x26);       //Gamma curve selected
    TFT_SEND_DATA(0x01);
 
  TFT_SEND_CMD(0xE0);       //Set Gamma
    TFT_SEND_DATA(0x0F);
    TFT_SEND_DATA(0x31);
    TFT_SEND_DATA(0x2B);
    TFT_SEND_DATA(0x0C);
    TFT_SEND_DATA(0x0E);
    TFT_SEND_DATA(0x08);
    TFT_SEND_DATA(0x4E);
    TFT_SEND_DATA(0xF1);
    TFT_SEND_DATA(0x37);
    TFT_SEND_DATA(0x07);
    TFT_SEND_DATA(0x10);
    TFT_SEND_DATA(0x03);
    TFT_SEND_DATA(0x0E);
    TFT_SEND_DATA(0x09);
    TFT_SEND_DATA(0x00);
 
  TFT_SEND_CMD(0xE1);       //Set Gamma
    TFT_SEND_DATA(0x00);
    TFT_SEND_DATA(0x0E);
    TFT_SEND_DATA(0x14);
    TFT_SEND_DATA(0x03);
    TFT_SEND_DATA(0x11);
    TFT_SEND_DATA(0x07);
    TFT_SEND_DATA(0x31);
    TFT_SEND_DATA(0xC1);
    TFT_SEND_DATA(0x48);
    TFT_SEND_DATA(0x08);
    TFT_SEND_DATA(0x0F);
    TFT_SEND_DATA(0x0C);
    TFT_SEND_DATA(0x31);
    TFT_SEND_DATA(0x36);
    TFT_SEND_DATA(0x0F);
 
  TFT_SEND_CMD(0x11);       //Exit Sleep
      for (int i=0;i<1000000; i++);
 
  TFT_SEND_CMD(0x29);    //Display on
  TFT_SEND_CMD(0x2C);
  
  }
 
/*--------------------------------------------------Функция включения/выключения светодиодной подсветки--------------------------*/
void TFT_LED (int tftled){if(tftled) {GPIOC->BSRR |= GPIO_BSRR_BS1;} else GPIOC->BSRR |= GPIO_BSRR_BR1;}
 
/*--------------------------------------------------Установка столбцов-----------------------------------------------------------*/
void TFT_SET_COLUMNS (int STARTcol, int ENDcol) {TFT_SEND_CMD(0x2A); TFT_SEND_WORD(STARTcol); TFT_SEND_WORD(ENDcol);}
 
/*-------------------------------------------------Установка строк---------------------------------------------------------------*/
void TFT_SET_PAGES (int STARTpage, int ENDpage) {TFT_SEND_CMD(0x2B); TFT_SEND_WORD(STARTpage); TFT_SEND_WORD(ENDpage);}
 
/*-------------------------------------------------Оси ХУ------------------------------------------------------------------------*/
void TFT_SET_XY (int poX, int poY) {TFT_SET_COLUMNS(poX, poX); TFT_SET_PAGES(poY, poY); TFT_SEND_CMD(0x2C);}
 
/*-------------------------------------------------Установка группы пикселей-----------------------------------------------------*/
void TFT_SET_PIXELS (int poX, int poY, int color) {TFT_SET_XY(poX, poY); TFT_SEND_WORD(color);}
 
 
 
int main () {
  
    RCC_Init();
    GPIO_LEDInit ();
    
   
  
int i,j;
   
  TFT_INIT();
  TFT_LED(1);
  
  
  for(i=150; i<200; i++) {
        for(j=150; j<200; j++) {
            TFT_SET_PIXELS(i, j, RED);
        }
    }
  
  Blue_On(1);
  
  
while(1);
 
}
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
04.12.2018, 08:06
Ответы с готовыми решениями:

Вывод кириллических символов на tft ili9341 spi, stm32f103
Доброго времени суток, не получается вывести, а точнее создать базу символов с кириллическими буквами для tft экрана. До этого всегда...

STM32F429-Disco TFT Плывет картинка по SPI
Добрый день Проявился непонятный эффект. Плата STM32F429i-disco с дисплеем на ili9341 режим 4-wire. Работало нормально до того...

Пример работы с tft экраном по spi DMA для hal
Доброго времени суток. Может кто поделится кодом, для работы с tft 320x240 ili9341 по spi с применением DMA (stm32f103)? на HAL...

55
 Аватар для _SayHello
874 / 535 / 175
Регистрация: 30.07.2015
Сообщений: 1,739
10.12.2018, 11:42
Студворк — интернет-сервис помощи студентам
Vozz, используй функцию sprintf из stdio.h, она преобразует в строку твое число, а потом строку и выводи

C
1
2
uint8_t temp[10];
sprintf(temp, "%d", value);
1
1 / 1 / 0
Регистрация: 22.06.2016
Сообщений: 96
10.12.2018, 18:22  [ТС]
Спасибо, помогло!

Добавлено через 3 часа 35 минут
А вот еще подскажите. К примеру решил я не затрагивать процессор и отправлять в TFT буквы и строки через DMA.
То есть:
1. Настраиваю DMA канал;
2. Включаю прерывание по событию в настройках DMA;
3. Срабатывает прерывание SPI1_IRQHandler() и уже внутри прерывания в SPI1 отправляются буквы и строки.

Получается, когда сработает SPI1_IRQHandler(), то процессор совсем не отвлечется и продолжит заниматься своими делами ?
0
Почетный модератор
 Аватар для Humanoid
11561 / 4356 / 453
Регистрация: 12.06.2008
Сообщений: 12,457
10.12.2018, 19:02
Цитата Сообщение от Vozz Посмотреть сообщение
То есть:
1. Настраиваю DMA канал;
2. Включаю прерывание по событию в настройках DMA;
3. Срабатывает прерывание SPI1_IRQHandler() и уже внутри прерывания в SPI1 отправляются буквы и строки.
Получается, когда сработает SPI1_IRQHandler(), то процессор совсем не отвлечется и продолжит заниматься своими делами ?
Нет.
1) Вы подготавливаете данные для передачи.
2) Запускаете DMA. И после этого процессор может заниматься своими делами.
3) Когда все данные будут переданы, то сработает прерывание.
0
Эксперт .NET
 Аватар для Rius
13845 / 7887 / 1706
Регистрация: 25.05.2015
Сообщений: 23,942
Записей в блоге: 14
10.12.2018, 19:46
Vozz, у TFT нет ни букв, ни строк. Есть только сырые растровые данные.
0
 Аватар для _SayHello
874 / 535 / 175
Регистрация: 30.07.2015
Сообщений: 1,739
10.12.2018, 20:32
Vozz, все как Humanoid, сказал, только надо не забыть CS вниз дёрнуть перед отправкой, а в конце передачи по прерыванию DMA обратно вверх поднять
0
1 / 1 / 0
Регистрация: 22.06.2016
Сообщений: 96
12.12.2018, 10:49  [ТС]
Все равно через DMA что-то не получается... Отладчик виснет по завершению инита DMA_CH3, SPI1->DR переполнен до FF, а ногами SPI1 дрыгать даже и не думает. Напрямик через SPI1 все классно

main.c

Кликните здесь для просмотра всего текста

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include "ILI9341.h"
 
 
void DMA1_Channel3_IRQHandler() {
  
  if(DMA1->ISR & DMA_ISR_TCIF3) {
     
    GPIOC->BSRR = GPIO_BSRR_BS8;
    
    DMA1->IFCR &= ~DMA_IFCR_CTCIF3;
    
    //TFT_DrawVerticalLine (40, 40, 150, 40,  RED);
    
  }
  
}
 
 
 
void rcc_init (void) {
  
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);                      // Enable HSE
    while (!(RCC->CR & RCC_CR_HSERDY));                 // Ready start HSE      
    
    FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;          // Cloclk Flash memory
    
    RCC->CFGR |= RCC_CFGR_HPRE_DIV1;                    // AHB = SYSCLK/1
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV1;                   // APB1 = HCLK/1
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;                   // APB2 = HCLK/1
    
    RCC->CFGR &= ~RCC_CFGR_PLLMULL;                             // clear PLLMULL bits
    RCC->CFGR &= ~RCC_CFGR_PLLSRC;                      // clearn PLLSRC bits
    RCC->CFGR &= ~RCC_CFGR_PLLXTPRE;                    // clearn PLLXTPRE bits
    
    RCC->CFGR |= RCC_CFGR_PLLSRC_PREDIV1;                   // source HSE
    RCC->CFGR |= RCC_CFGR_PLLXTPRE_PREDIV1_Div2;                // source HSE/2 = 4 MHz
    RCC->CFGR |= RCC_CFGR_PLLMULL6;                     // PLL x6: clock = 4 MHz * 6 = 24 MHz
    
    RCC->CR |= RCC_CR_PLLON;                                    // enable PLL
    while((RCC->CR & RCC_CR_PLLRDY) == 0) {}                    // wait till PLL is ready
    
    RCC->CFGR &= ~RCC_CFGR_SW;                                  // clear SW bits
        RCC->CFGR |= RCC_CFGR_SW_PLL;                                   // select source SYSCLK = PLL
    while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_1) {}          // wait till PLL is used
        
        //Тактирование периферии
  
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN | RCC_APB2ENR_SPI1EN;//Тактируем порты A,B,C, регистр альтернативных функций выводов, АЦП, SPI1 и TIM1
  
RCC->AHBENR |= RCC_AHBENR_DMA1EN; //Тактируем DMA1
 
}
 
void ALLpins_init () { 
  
  //PA0 - CS(3),PA1-DC(5), PA2 - RST(4),PA3 - LED, PA5 _SCK(7),PA6 - MISO(9),PA7 - MOSI(6)
  
GPIOC->CRL &= ~GPIO_CRL_CNF0;    GPIOC->CRL &= ~GPIO_CRL_CNF1;
GPIOC->CRL |= GPIO_CRL_MODE0;    GPIOC->CRL |= GPIO_CRL_MODE1;
 
GPIOC->CRH &= ~GPIO_CRH_CNF8;    GPIOC->CRH &= ~GPIO_CRH_CNF9;
GPIOC->CRH |= GPIO_CRH_MODE8;    GPIOC->CRH |= GPIO_CRH_MODE9;
   
GPIOA->CRL &= ~GPIO_CRL_CNF2;    GPIOA->CRL &= ~GPIO_CRL_CNF3;
GPIOA->CRL |= GPIO_CRL_MODE2;    GPIOA->CRL |= GPIO_CRL_MODE3;
   
   
     //вывод SCK: выход двухтактный, альтернативная функция, 50MHz
GPIOA->CRL   |=  GPIO_CRL_MODE5; GPIOA->CRL   &= ~GPIO_CRL_CNF5; GPIOA->CRL   |=  GPIO_CRL_CNF5_1;   
 
//вывод MISO: вход цифровой с подтягивающим резистором, подтяжка к плюсу
GPIOA->CRL   &= ~GPIO_CRL_MODE6; GPIOA->CRL   &= ~GPIO_CRL_CNF6; GPIOA->CRL   |=  GPIO_CRL_CNF6_1; GPIOA->BSRR   =  GPIO_BSRR_BS6;     
 
//вывод MOSI: выход двухтактный, альтернативная функция, 50MHz
GPIOA->CRL   |=  GPIO_CRL_MODE7; GPIOA->CRL   &= ~GPIO_CRL_CNF7; GPIOA->CRL   |=  GPIO_CRL_CNF7_1;   
 
}
 
 
void main () { 
  
  rcc_init ();
  ALLpins_init ();
  spi1_init ();
  TFT_init();
  
  TFT_led(1);
  
 
  
  
  
  while(1);
  
}
C++
1
 

ILI9341.c

Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
#include "ILI9341.h"
 
 
void spi1_init () { 
  
  SPI1->CR1 &= ~SPI_CR1_BIDIMODE;
  SPI1->CR1 &= ~SPI_CR1_BIDIOE;
  SPI1->CR1    |= SPI_CR1_MSTR;       //контроллер должен быть мастером 
  SPI1->CR1 &= ~SPI_CR1_DFF;               //8 bit data
  SPI1->CR1 &= ~SPI_CR1_CPOL;             //Polarity cls signal CPOL = 0;
  SPI1->CR1 &= ~SPI_CR1_CPHA;             //Phase cls signal    CPHA = 0;
  SPI1->CR1    |= SPI_CR1_SSI;        //обеспечить высокий уровень программного NSS
  SPI1->CR1    |= SPI_CR1_SSM;        //разрешить программное формирование NSS
  SPI1->CR1 |= SPI_CR1_BR_2; // fpclk/32
  SPI1->CR1 &= ~SPI_CR1_LSBFIRST;
  SPI1->CR2 |= SPI_CR2_TXDMAEN;//Разрешили передачу по DMA1_CH3
  SPI1->CR1    |= SPI_CR1_SPE;        //разрешить работу модуля SPI1
  
}
 
 
void spi8 () { 
  
  SPI1->CR1 &= ~SPI_CR1_SPE; //Отключили SPI1
  SPI1->CR1 &= ~SPI_CR1_DFF;//Передача 8 бит
  SPI1->CR1 |= SPI_CR1_SPE; //Включили SPI1
  
}
 
void spi16 () { 
  
  SPI1->CR1 &= ~SPI_CR1_SPE; //Отключили SPI1
  SPI1->CR1 |= SPI_CR1_DFF;//Передача 16 бит
  SPI1->CR1 |= SPI_CR1_SPE; //Включили SPI1
  
}
 
 
void sendDMA8(uint8_t data) {
    
        
        //spi8 ();
    DMA1_Channel3->CCR |= DMA_CCR3_DIR; //направление передачи из памяти в периферию
    DMA1_Channel3->CPAR |= (uint32_t)(&SPI1->DR); //В SPI1
    DMA1_Channel3->CMAR = (uint32_t)data; //е
    DMA1_Channel3->CNDTR = 1; //Количество пересылаемых значений = 1
    DMA1_Channel3->CCR |= DMA_CCR3_MINC;// Инкрементация памяти после каждой передачи включена
    DMA1_Channel3->CCR &= ~DMA_CCR3_PSIZE;//Размерность данных периферии 8 бит
        DMA1_Channel3->CCR &= ~DMA_CCR3_MSIZE;//Размерность данных памяти 8 бит
    DMA1_Channel3->CCR |= DMA_CCR3_PL; //Приоритет - очень высокий 
        DMA1_Channel3->CCR &= ~DMA_CCR3_CIRC; //Запрещвем работу DMA в циклическом режиме
        DMA1_Channel3->CCR |= DMA_CCR3_TCIE;//Разрешили прерывание после передачи 8 бит
        NVIC_EnableIRQ(DMA1_Channel3_IRQn); //общее разрешение прерываний
         __enable_irq(); //Глобальное разрешение прерываний
        DMA1_Channel3->CCR |= DMA_CCR3_EN; //Разрешаем работу 3-го канала DMA
        
}
 
void sendDMA16(uint16_t data) {
    
        
        //spi16 ();
    DMA1_Channel3->CCR |= DMA_CCR3_DIR; //направление передачи из памяти в периферию
    DMA1_Channel3->CPAR |= (uint32_t)(&SPI1->DR); //В SPI1
    DMA1_Channel3->CMAR = (uint32_t)data; //е
    DMA1_Channel3->CNDTR = 1; //Количество пересылаемых значений = 1
    DMA1_Channel3->CCR |= DMA_CCR3_MINC;// Инкрементация памяти после каждой передачи включена
    DMA1_Channel3->CCR &= ~DMA_CCR3_PSIZE;//Размерность данных периферии 8 бит
        DMA1_Channel3->CCR &= ~DMA_CCR3_MSIZE;//Размерность данных памяти 8 бит
    DMA1_Channel3->CCR |= DMA_CCR3_PL; //Приоритет - очень высокий 
        DMA1_Channel3->CCR &= ~DMA_CCR3_CIRC; //Запрещвем работу DMA в циклическом режиме
        DMA1_Channel3->CCR |= DMA_CCR3_TCIE;//Разрешили прерывание после передачи 8 бит
        NVIC_EnableIRQ(DMA1_Channel3_IRQn); //общее разрешение прерываний
         __enable_irq(); //Глобальное разрешение прерываний
        DMA1_Channel3->CCR |= DMA_CCR3_EN; //Разрешаем работу 3-го канала DMA
        
}
 
 
 
 
 
/*-----------------------------------------------Инит TFT-------------------------------------------------------------------------------*/
void TFT_init() {
     
    TFT_RST_LOW;
    for(int i=0; i<0x0FFFF; i++);
    TFT_RST_HIGH;
 
    TFT_sendCMD(0xCB);
    TFT_sendDATA(0x39);
    TFT_sendDATA(0x2C);
    TFT_sendDATA(0x00);
    TFT_sendDATA(0x34);
    TFT_sendDATA(0x02);
 
    TFT_sendCMD(0xCF);
    TFT_sendDATA(0x00);
    TFT_sendDATA(0xC1);
    TFT_sendDATA(0x30);
 
    TFT_sendCMD(0xE8);
    TFT_sendDATA(0x85);
    TFT_sendDATA(0x00);
    TFT_sendDATA(0x78);
 
    TFT_sendCMD(0xEA);
    TFT_sendDATA(0x00);
    TFT_sendDATA(0x00);
 
    TFT_sendCMD(0xED);
    TFT_sendDATA(0x64);
    TFT_sendDATA(0x03);
    TFT_sendDATA(0x12);
    TFT_sendDATA(0x81);
 
    TFT_sendCMD(0xF7);
    TFT_sendDATA(0x20);
 
    TFT_sendCMD(0xC0);      //Power control
    TFT_sendDATA(0x23);     //VRH[5:0]
 
    TFT_sendCMD(0xC1);      //Power control
    TFT_sendDATA(0x10);     //SAP[2:0];BT[3:0]
 
    TFT_sendCMD(0xC5);      //VCM control
    TFT_sendDATA(0x3e);     //Contrast
    TFT_sendDATA(0x28);
 
    TFT_sendCMD(0xC7);      //VCM control2
    TFT_sendDATA(0x86);      //--
 
    TFT_sendCMD(0x36);      // Memory Access Control
    TFT_sendDATA(0x48);     //C8       //48 68з»”ж*§зќ†//28 E8 е¦ЇоЃ„зќ†
 
    TFT_sendCMD(0x3A);
    TFT_sendDATA(0x55);
 
    TFT_sendCMD(0xB1);
    TFT_sendDATA(0x00);
    TFT_sendDATA(0x18);
 
    TFT_sendCMD(0xB6);      // Display Function Control
    TFT_sendDATA(0x08);
    TFT_sendDATA(0x82);
    TFT_sendDATA(0x27);
 
    TFT_sendCMD(0xF2);      // 3Gamma Function Disable
    TFT_sendDATA(0x00);
 
    TFT_sendCMD(0x26);      //Gamma curve selected
    TFT_sendDATA(0x01);
 
    TFT_sendCMD(0xE0);      //Set Gamma
    TFT_sendDATA(0x0F);
    TFT_sendDATA(0x31);
    TFT_sendDATA(0x2B);
    TFT_sendDATA(0x0C);
    TFT_sendDATA(0x0E);
    TFT_sendDATA(0x08);
    TFT_sendDATA(0x4E);
    TFT_sendDATA(0xF1);
    TFT_sendDATA(0x37);
    TFT_sendDATA(0x07);
    TFT_sendDATA(0x10);
    TFT_sendDATA(0x03);
    TFT_sendDATA(0x0E);
    TFT_sendDATA(0x09);
    TFT_sendDATA(0x00);
 
    TFT_sendCMD(0xE1);      //Set Gamma
    TFT_sendDATA(0x00);
    TFT_sendDATA(0x0E);
    TFT_sendDATA(0x14);
    TFT_sendDATA(0x03);
    TFT_sendDATA(0x11);
    TFT_sendDATA(0x07);
    TFT_sendDATA(0x31);
    TFT_sendDATA(0xC1);
    TFT_sendDATA(0x48);
    TFT_sendDATA(0x08);
    TFT_sendDATA(0x0F);
    TFT_sendDATA(0x0C);
    TFT_sendDATA(0x31);
    TFT_sendDATA(0x36);
    TFT_sendDATA(0x0F);
 
    TFT_sendCMD(0x11);      //Exit Sleep
    for(int i=0; i<0x00FFF; i++);
 
    TFT_sendCMD(0x29);    //Display on
    TFT_sendCMD(0x2c);
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
 
/*---------------------------------------------------Функции отправки----------------------------------------------------------------------------*/
void TFT_sendByte(uint8_t data)
{
 
    while(!(SPI1->SR & SPI_SR_TXE));
    //SPI1->DR =  data;
    sendDMA8(data);
}
 
void TFT_sendCMD(int index)
{
// ждем чтобы старые данные ушли до того как мы поменяем состояние линий управления дисплеем
 
    while(SPI1->SR & SPI_SR_BSY);
    
    TFT_DC_LOW;
    TFT_CS_LOW;
 
    sendDMA8(index);
    TFT_CS_HIGH;
}
 
void TFT_sendDATA(int data)
{
  
  while(SPI1->SR & SPI_SR_BSY);
    TFT_DC_HIGH;
    sendDMA8(data);
    TFT_CS_HIGH;
}
 
void TFT_sendWord(uint16_t data)
{
 
    while(SPI1->SR & SPI_SR_BSY);
    TFT_DC_HIGH;
 
    sendDMA16(data);
    TFT_CS_HIGH;
}
 
void TFT_led(int state)
{
    if (state)
        GPIOA->BSRR = GPIO_BSRR_BS3;
    else
        GPIOA->BSRR = GPIO_BSRR_BR3;
}
 
void TFT_setCol(int StartCol, int EndCol)
{
    TFT_sendCMD(0x2A);                                                      // Column Command address
    TFT_sendWord(StartCol);
    TFT_sendWord(EndCol);
}
 
void TFT_setPage(int StartPage, int EndPage)
{
    TFT_sendCMD(0x2B);                                                      // Column Command address
    TFT_sendWord(StartPage);
    TFT_sendWord(EndPage);
}
 
void TFT_setXY(int poX, int poY)
{
    TFT_setCol(poX, poX);
    TFT_setPage(poY, poY);
    TFT_sendCMD(0x2c);
}
 
void TFT_setPixel(int poX, int poY, int color)
{
    TFT_setXY(poX, poY);
    TFT_sendWord(color);
}
 
 
/*-----------------------------------------------------Вертикальная линия---------------------------------------------------------------*/
 
void TFT_DrawVerticalLine (uint16_t PosX, uint16_t PosY, uint16_t height, uint16_t thickness, uint16_t colour)  {
  
    int i=0,j=0;
  
    if (thickness>0) {
  
  for (i=PosY; i<height+PosY; i++) {
    for (j=PosX;j<thickness+PosX;j++) {
      
    TFT_setPixel(i,j, colour);
    
 
  } 
    }
      }
        }
 
/*-----------------------------------------------------Горизонтальная линия---------------------------------------------------------------*/
 
void TFT_DrawHorizontalLine (uint16_t PosX, uint16_t PosY, uint16_t width, uint16_t thickness, uint16_t colour)  {
  
    int i=0,j=0;
  
    if (thickness>0) {
  
  for (i=PosX; i<width+PosX; i++) {
    for (j=PosY;j<thickness+PosY;j++) {
      
    TFT_setPixel(j,i, colour);
    
 
  } 
    }
      }
        }
 
 
 
/*-----------------------------------------------------Пустой прямоугольник---------------------------------------------------------------*/
 
void TFT_DrawRectangle (uint16_t PosX, uint16_t PosY, uint16_t width, uint16_t height, uint16_t thickness, uint16_t colour)  {
  
  TFT_DrawHorizontalLine (PosX, PosY,  width, thickness, colour);
  TFT_DrawVerticalLine (PosX,  PosY,  height, thickness, colour);
  TFT_DrawHorizontalLine (PosX, PosY+height,  width+thickness, thickness, colour);
  TFT_DrawVerticalLine (PosX+width,  PosY,  height+thickness, thickness, colour);
  
        }
0
 Аватар для _SayHello
874 / 535 / 175
Регистрация: 30.07.2015
Сообщений: 1,739
12.12.2018, 11:35
Vozz, ну во первых, TFT_CS_HIGH; нужно поднимать в прерывании по завершению передачи DMA, а не после конфигурации DMA. Ибо физически данные еще не улетели,а вы уже CS поднимаете.
Во вторых, каждый раз полностью перезабивать параметры DMA не надо.
Достаточно один раз настроить
C
1
2
3
4
5
6
7
8
9
10
 DMA1_Channel3->CCR |= DMA_CCR3_DIR; //направление передачи из памяти в периферию
    DMA1_Channel3->CPAR |= (uint32_t)(&SPI1->DR); //В SPI1
    DMA1_Channel3->CCR |= DMA_CCR3_MINC;// Инкрементация памяти после каждой передачи включена
    DMA1_Channel3->CCR &= ~DMA_CCR3_PSIZE;//Размерность данных периферии 8 бит
    DMA1_Channel3->CCR &= ~DMA_CCR3_MSIZE;//Размерность данных памяти 8 бит
    DMA1_Channel3->CCR |= DMA_CCR3_PL; //Приоритет - очень высокий 
    DMA1_Channel3->CCR &= ~DMA_CCR3_CIRC; //Запрещвем работу DMA в циклическом режиме
    DMA1_Channel3->CCR |= DMA_CCR3_TCIE;//Разрешили прерывание после передачи 8 бит
    NVIC_EnableIRQ(DMA1_Channel3_IRQn); //общее разрешение прерываний
     __enable_irq(); //Глобальное разрешение прерываний
Заметьте, тут я не выставляю бит ENABLE.

Если хотим отправить байт делаем так:
C
1
2
3
       DMA1_Channel3->CMAR = (uint32_t)&data;
       DMA1_Channel3->CNDTR = 1;
       DMA1_Channel3->CCR |= DMA_CCR3_EN;
По приходу прерывания, отключать модуль
C
1
DMA1_Channel3->CCR &= ~DMA_CCR3_EN;
Вообще, смысл DMA - передача больших массивов данных, для передачи одного байта смысла особо нет. Команды можно и напрямую толкать в SPI, а так телодвижений на каждый байт больше, чем просто в SPI его толкнуть.
Смысл есть использовать DMA когда непосредственно отрисовывать картинки будешь, и загонять большие объемы данных туда.

+ не очень мне нравится, что адрес аргумента функции указывается как адрес откуда DMA будет кидать данные. Ведь по сути, эта переменная находится в стеке, а при завершении функции она из стека выгружается. Тут может роли и не сыграет ибо один байт может и успеет проскочить....

Обычно делают так, выделяют какой нибудь массивчик в куче, считай глобально, настраивают DMA на передачу из этого массивчика. Поток не включают.
Далее заполняют массив данными, полностью или нет не важно. И когда надо их отправить, устанавливают количество байт , которое нужно отправить из массива и устанавливают бит EN, читай включают поток. По приходу прерывания, чистят флаги и выключают поток снимая бит EN.

Добавлено через 17 минут
Vozz, как сделал бы я (тут я из вашего кода беру строки, все биты не проверял)

Создал бы глобальный массив для передачи
C
1
volatile uint8_t TxBuffer[256];
В main один раз проинициализировал DMA функцией? в качестве аргумента указатель на глобальный массив определенный выше:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void Init_TxDMA(uint8_t * source)
{
    RCC->AHBENR |= RCC_AHBENR_DMA1EN;
    
    DMA1_Channel3->CCR |= DMA_CCR3_DIR; //направление передачи из памяти в периферию
    DMA1_Channel3->CPAR |= (uint32_t)(&SPI1->DR); //В SPI1
    DMA1_Channel3->CMAR = (uint32_t)source;
    DMA1_Channel3->CCR |= DMA_CCR3_MINC;// Инкрементация памяти после каждой передачи включена
    DMA1_Channel3->CCR &= ~DMA_CCR3_PSIZE;//Размерность данных периферии 8 бит
    DMA1_Channel3->CCR &= ~DMA_CCR3_MSIZE;//Размерность данных памяти 8 бит
    DMA1_Channel3->CCR |= DMA_CCR3_PL; //Приоритет - очень высокий 
    DMA1_Channel3->CCR &= ~DMA_CCR3_CIRC; //Запрещвем работу DMA в циклическом режиме
    DMA1_Channel3->CCR |= DMA_CCR3_TCIE;//Разрешили прерывание после передачи 8 бит
    NVIC_EnableIRQ(DMA1_Channel3_IRQn); //общее разрешение прерываний
    __enable_irq(); //Глобальное разрешение прерываний
}
сделал бы функции отправки( я бы конечно не стал для одного байта DMA делать, но так, чтобы для примера):

C
1
2
3
4
5
6
void sendDMA8(uint8_t data) 
{
    TxBuffer[0] = data;
    DMA1_Channel3->CNDTR = 1;
    DMA1_Channel3->CCR |= DMA_CCR3_EN;
}
C
1
2
3
4
5
6
7
void TFT_sendDATA(int data)
{
    while(SPI1->SR & SPI_SR_BSY);
    TFT_DC_HIGH;
    TFT_CS_LOW;
    sendDMA8(data);
}
Ну и прерывание:

C
1
2
3
4
5
6
7
8
9
void DMA1_Channel3_IRQHandler() 
{
    if(DMA1->ISR & DMA_ISR_TCIF3) 
    {
        DMA1->IFCR &= ~DMA_IFCR_CTCIF3;
        DMA1_Channel3->CCR &= ~DMA_CCR3_EN;
        TFT_CS_HIGH;
    }
}
0
Модератор
Эксперт по электронике
8982 / 6749 / 921
Регистрация: 14.02.2011
Сообщений: 23,876
12.12.2018, 11:47
Цитата Сообщение от _SayHello Посмотреть сообщение
Ведь по сути, эта переменная находится в стеке, а при завершении функции она из стека выгружается.
а может и не в стеке
в отличии от 8086 у ARM много регистров, и локальные переменные могут лежать в них
Цитата Сообщение от _SayHello Посмотреть сообщение
Создал бы глобальный массив для передачи
можно и локальный, но static, по сути это тоже глобальный, но область видимости только в данной функции
0
1 / 1 / 0
Регистрация: 22.06.2016
Сообщений: 96
12.12.2018, 11:52  [ТС]
ОК, опробую этот вариант.

Вообще да, с одним байтом бы заморачиваться не стал, но у меня задача такая:
- Генерирую 3-фазный синус с выходов STM32F100 (в прерывании TIM6_DAC);
- Опрашиваю 2 канала ADC1_IN0 и ADC1_IN1 (по DMA1_CH1 и складываю в переменную во флеш);

Как раз вот эти измерения с ADC1_IN0, ADC1_IN1 понадобилось вывести на TFT. А там процессор если принимается за вывод строки измерения АЦП, то ему вообще не до синуса. Вот думаю как раз DMA и будет выходом в этом случае (ну либо второй МК лепить чисто для опроса АЦП и вывода на TFT).

Как по вашему мнению, потянет МК такой нагруз или все же правильнее с двумя МК?

Вывод фигуры на TFT в данном случае для примера, чтобы научиться пользоваться связкой SPI+DMA+TFT
0
 Аватар для _SayHello
874 / 535 / 175
Регистрация: 30.07.2015
Сообщений: 1,739
12.12.2018, 12:00
ValeryS,
Цитата Сообщение от ValeryS Посмотреть сообщение
а может и не в стеке
в отличии от 8086 у ARM много регистров, и локальные переменные могут лежать в них
Я честно говоря не разу не проверял и грабли не ловил, но из логики просто не стал бы локальные переменные в DMA пихать.

Тут есть еще одна штука которую я забыл упомянуть.... DMA пихает данные в DR SPI когда, SPI устанавливает флаг TXE, то есть данные вытолкнули, но они не факт что доехали. Между байтами отправки все нормально, а вот последний байт еще может не доехать, DMA отрапортует что оно все отправило и мы пин поднимем CS ... не хорошо так)

Vozz,
Цитата Сообщение от Vozz Посмотреть сообщение
- Генерирую 3-фазный синус с выходов STM32F100 (в прерывании TIM6_DAC);
А зачем его генерировать в прерывании? Сделал массив на период синуса, заполнил его синусом, настраиваешь таймер на частоту дискретизации и заставляешь его пинать DMA, который кладет все в DAC
0
1 / 1 / 0
Регистрация: 22.06.2016
Сообщений: 96
12.12.2018, 12:03  [ТС]
Цитата Сообщение от _SayHello Посмотреть сообщение
А зачем его генерировать в прерывании?
Тогда я обходил DMA стороной )
0
 Аватар для _SayHello
874 / 535 / 175
Регистрация: 30.07.2015
Сообщений: 1,739
12.12.2018, 12:08
Vozz, есть вариант устроить рефакторинг)

Добавлено через 3 минуты
Vozz, рекомендую к прочтению апноут от производителя https://www.st.com/content/ccc... 259245.pdf
0
1 / 1 / 0
Регистрация: 22.06.2016
Сообщений: 96
12.12.2018, 12:24  [ТС]
Эммм.. DAC в моем случае неприменим. так как для 3-ф синуса нужно 6 ног (он двухполярный), а выходы таймера сразу на драйвер идут. Там с выходов драйвера на фильтры и нагрузка примерно по 50 Вт на фазу.
DAC как я понимаю, наиболее применим для звуковой техники, а у меня инвертор для проверки РЗА.
Так что лучше пусть пока останется как есть, а с SPI+DMA+TFT поэкпериментирую).
0
 Аватар для _SayHello
874 / 535 / 175
Регистрация: 30.07.2015
Сообщений: 1,739
12.12.2018, 12:32
Vozz, а все понял, в любом случае можно генерить синус скважностью ШИМ тоже через DMA. Недавно нужно было завести небольшой усилок на который двухполярный синус надо было дать

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include "stm32f10x.h"
#include "math.h"
 
uint32_t LowFrec = 1000;
uint32_t HighFrec = 400000;
 
uint16_t Samples[500];
 
int main(void)
{
    SystemInit();
    
    uint16_t NumSamples = HighFrec / LowFrec;
 
    
    for(uint16_t i = 0; i < NumSamples; i++)
    {
        Samples[i] = (uint16_t)roundf(90*(sin( i * 2*3.14 / NumSamples)) + 90);
    }   
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);    
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    
    
    GPIO_InitTypeDef pins;
    
    pins.GPIO_Pin       =       GPIO_Pin_8 | GPIO_Pin_9;
    pins.GPIO_Mode      =       GPIO_Mode_AF_PP;
    pins.GPIO_Speed     =       GPIO_Speed_50MHz;
    GPIO_Init(GPIOE, &pins);
    
    GPIO_PinRemapConfig(GPIO_FullRemap_TIM1, ENABLE); 
 
    DMA_InitTypeDef DMA_InitStructure;
 
    /* DMA1 Channel5 Config */
    DMA_DeInit(DMA1_Channel5);
 
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&TIM1->CCR1;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Samples;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = NumSamples;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
 
    DMA_Init(DMA1_Channel5, &DMA_InitStructure);
 
    /* DMA1 Channel5 enable */
    DMA_Cmd(DMA1_Channel5, ENABLE);
 
    TIM_TimeBaseInitTypeDef timer;
    
    timer.TIM_Period        =       180 - 1;
    timer.TIM_CounterMode   =       TIM_CounterMode_Up;
    timer.TIM_ClockDivision =       TIM_CKD_DIV1;
    timer.TIM_Prescaler     =       0;
    timer.TIM_RepetitionCounter =   0;
    TIM_TimeBaseInit(TIM1, &timer);
    
    TIM_OCInitTypeDef   PWM;
    
    PWM.TIM_OCMode          =           TIM_OCMode_PWM1;
    PWM.TIM_OCIdleState     =           TIM_OCIdleState_Set;
    PWM.TIM_OCNIdleState    =           TIM_OCIdleState_Set;
    PWM.TIM_OCPolarity      =           TIM_OCPolarity_High;
    PWM.TIM_OCNPolarity     =           TIM_OCPolarity_High;
    PWM.TIM_OutputNState    =           TIM_OutputNState_Enable;
    PWM.TIM_OutputState     =           TIM_OutputState_Enable;
    PWM.TIM_Pulse           =           0;
    TIM_OC1Init(TIM1, &PWM);
    
    TIM_DMACmd(TIM1, TIM_DMA_Update, ENABLE);
    
    TIM_Cmd(TIM1, ENABLE);
    
    TIM_CtrlPWMOutputs(TIM1, ENABLE);   
 
 
 
 
    while(1)
    {
    }
}
Тут генерируется синус на 1 кГц с частотой дискретизации 400 кГц
0
1 / 1 / 0
Регистрация: 22.06.2016
Сообщений: 96
12.12.2018, 12:46  [ТС]
Хм. С помощью math.h и никакого массива. Когда то пытался так сделать, приму на заметку!

Мое решение. Кстати принцип прохода по массиву подсказали на этом же форуме

Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include "Sinus.h"
#include "stm32f10x.h"
 
unsigned int sin[256] = {0,12,25,35,50,62,74,86,97,109,120,131,141,151,161,171,180,188,196,204,211,
218,224,230,235,239,243,246,249,251,253,254 /*32*/,
254,254,253,251,249,246,243,239,235,230,224,218,211,204,196,188,180,171,161,151,141,131,120,109,97,86,74,62,50,35,25,12,/*64*/
0,12,25,35,50,62,74,86,97,109,120,131,141,151,161,171,180,188,196,204,211,
218,224,230,235,239,243,246,249,251,253,254 /*96*/,
254,254,253,251,249,246,243,239,235,230,224,218,211,204,196,188,180,171,161,151,141,131,120,109,97,86,74,62,50,35,25,12,/*128*/};
 
int ax=0,bx=0,cx=0; //Переменные для прохода по массиву синуса
int A1x,A2x,A3x,A4x,A5x,A6x; //Переменные. для отслеживания содержимого CCRx счетчиков по напряжениям А,В,С
 
int Ua=1, Ub=1, Uc=1; //Амплитуда фаз напряжения А,B,C
int  phUa = 0, phUb=43, phUc=86; //Начальная фаза напряжений А,В,С
float freqU=750; //Частота sin U
 
 
/*-------------------------------------------------Формирование фазы А--------------------------------------------------------*/
void Form_Ua () {
 
                                         /*PA8 - Ua1, PA9 - Ua2*/
 
 ax++;
 if (ax<phUa+64) {TIM1->CCR1 = Ua*sin[ax-phUa];A1x=TIM1->CCR1;} // Это положительная полуволна фазы А
 if(ax>phUa+64) {TIM1->CCR1 = 0; A1x = 0; TIM1->CCR2 = Ua*sin[ax-phUa-64]; A2x=TIM1->CCR2;}// Это отрицательная полуволна фазы А
 if(ax>phUa+128) {ax = phUa; TIM1->CCR1 = 0; A1x = 0; TIM1->CCR2 = 0; A2x = 0;} // Ограничение. Если досчитали до 128-ми, сбрасываем счет до нуля и по-новой!!!
 
}
 
/*----------------------------------------------Формирование фазы В------------------------------------------------------------*/
void Form_Ub () {
 
                                           /*PA10 - Ub1, PA11 - Ub2*/
  
 bx++;
 if (bx>phUb) {TIM1->CCR3 = Ub*sin[bx-phUb];A3x=TIM1->CCR3;} //Это положительная полуволна фазы B
 if(bx>phUb+64) {TIM1->CCR3 = 0; A3x = 0; TIM1->CCR4 = Ub*sin[bx-phUb-64]; A4x=TIM1->CCR4;}//Это отрицательная полуволна фазы B
 if(bx>phUb+128) {bx = phUb; TIM1->CCR3 = 0; A3x = 0; TIM1->CCR4 = 0; A4x = 0;} // Ограничение. Если досчитали до 128-ми, сбрасываем счет до нуля и по-новой!!!
 
}
 
 
/*----------------------------------------------Формирование фазы C------------------------------------------------------------*/
void Form_Uc () {
  
                                            /*PB0 - Uc1, PB1 - Uc2*/
  
 cx++;
 if (cx>phUc) {TIM3->CCR3 = Uc*sin[cx-phUc];A5x=TIM3->CCR3;} //Это положительная полуволна фазы C
 if(cx>phUc+64) {TIM3->CCR3 = 0; A5x = 0; TIM3->CCR4 = Uc*sin[cx-phUc-64]; A6x=TIM3->CCR4;}//Это отрицательная полуволна фазы С
 if(cx>phUc+128) {cx = phUc; TIM3->CCR3 = 0; A5x = 0; TIM3->CCR4 = 0; A6x = 0;} // Ограничение. Если досчитали до 128-ми, сбрасываем счет до нуля и по-новой!!!
  
}



Не пойму как у вас сдвигается положительная полуволна по отношению к отрицательной ...
0
 Аватар для _SayHello
874 / 535 / 175
Регистрация: 30.07.2015
Сообщений: 1,739
12.12.2018, 12:47
Vozz, и кстати на stm32f100rb TIM1 имеет три канала + их инверсные каналы, то есть треххфазный синус + его инверсию можно реализовать на одном таймере + 3 таблицы синуса + 3 канала DMA
0
1 / 1 / 0
Регистрация: 22.06.2016
Сообщений: 96
12.12.2018, 12:53  [ТС]
Да, но опять же ...
Записал я в TIM1->CCR1 лог 1. Нога таймера дернулась. Записал лог "0", дернулся комплементарный вывод. И так дальше. Если разбивать на 128 бит этот синус, то комплементарный вывод становится уже не актуальным )

Добавлено через 1 минуту
То есть, если я буду писать 128-битный синус в TIM1->CCR1, то на комплементарном выводе будет инвертированное отображение таблицы...
0
 Аватар для _SayHello
874 / 535 / 175
Регистрация: 30.07.2015
Сообщений: 1,739
12.12.2018, 12:56
Vozz, не понял, у TIM1 имеет прямой и комплиментарный выход на каждый канал. CCR1 регистр сравнения, туда мы записываем скважность ШИМ. текущего отсчета синуса, дальше на прямом выходе идет генерация этого ШИМ, а на комплиментарном выходе его инверс. По переполнению таймера (на следующем периоде ШИМ), загружаются следующие значения скважности из таблицы.
0
1 / 1 / 0
Регистрация: 22.06.2016
Сообщений: 96
12.12.2018, 13:14  [ТС]
В общем, вот такой сигнал было необходимо получить, а как это сделать с помощью прямого и комплементарного выводов TIM1, я как то не додумался... Хотя что-то мне подсказывало, что не зря комплементарный вывод есть )
Миниатюры
Вывод информации на TFT через SPI  
0
1 / 1 / 0
Регистрация: 22.06.2016
Сообщений: 96
12.12.2018, 13:20  [ТС]
То есть сдвиг двух полуволн под 180 град по отношению друг к другу
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
12.12.2018, 13:20

Вывод информации из бд через ссылку
Добрый день. Сразу хочу отметить что мой вопрос может показаться лёгким для некоторой части людей. Однако хочу заметить что используя PDO...

Вывод информации из БД через форму
Всем привет. Я новичок в php и прошу у вас помощи. Необходимо сделать следующий скрипт php -&gt; Пользователь вводит в форму...

Вывод информации из БД через форму
Всем привет. Я новичок в php и прошу у вас помощи. Необходимо сделать следующий скрипт php -&gt; Пользователь вводит в форму...

Вывод информации через MS Word
Здраствуйте! Подскажите плз как вывести информацию из БД через MS Word. Заранее большое спасибо!

Вывод информации через скрипт
Возникла у меня тут пара вопросов относительно вывода информации через скрипт. Первое - я никак не могу понять как обратится к объекту не...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Мобильное приложение ColorStep
pavlinmavlin 17.09.2026
Реализовал приложение Красный, Зеленый, Синий в Unity3d + c#. Название изменил на ColorStep. Приложение прошло модерацию и теперь доступно для скачивания. Делал его сам, шаг за шагом — и вот,. . .
Запрет дублирования строк в табличной части
Maks 13.09.2026
Реализация из решения ниже выполнена на нетиповом справочнике "Нормы ТО" с табличной часть "Виды ТО", разработанного в КА2, со следующими реквизитами: - ВидТО (СправочникСсылка. ВидыТО); - ВидГСМ. . .
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
Программа опроса у.з. расходомера SLS-720F
Argus19 02.09.2026
Программа опроса у. з. расходомера SLS-720F Программа опрашивает один раз в минуту три ультразвуковых расходомера SLS-720F через интерфейс RS-485 по протоколу Modbus RTU. Опрашиваются регистры. . .
Hyper-V: Компьютер должен поддерживать доверенный платформенный модуль 2.0.
Maks 31.08.2026
При установке Windows 11 на виртуальную машину Hyper-V 2-го поколения вылезла такая ошибка: Решение: в параметрах виртуальной машины, в разделе "Безопасность" (Security) активировать флаг. . .
Архитектура биовида Стива в Майнкрафте: Зачем бонобо кубический каннибализм
anaschu 30.08.2026
Кубический Вагинокапитализм в Minecraft: Математический инвариант ОДУ и рок Стивов-бонобо Главная задача разработанной «Модели Всего» — наглядно продемонстрировать наличие системной «судьбы». . .
Оттачиваю умение писать js программы.
russiannick 30.08.2026
Проектом выходного дня стало написание Книги шифров Виженера. Итогом стала версия 200, синий туман. Синий туман назван так, потому что замораживает текст под собой. Нажатие синих кнопок управляют. . .
мат медиц модель 30. презентация проекта
anaschu 27.08.2026
хоп хоп хоп хидахоп, а я кладую))
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru