+7(960) 250-82-68 spam@mirossa.ru


  MCU         C           1С         PHP       JAVA      разное 


Статьи
 
 
Работа по учебнику Discovering the STM32 Microcontroller by Geoffrey Brown. главы 7 + 8 + 12.

STM32 + ST7735 + SD card. Интерфейс SPI. DMA

При нажатии кнопки "на stm32" на дисплее последовательно отображаются: текстовая строка, изображения из bitmap файла(считываются с SD карты), графические примитивы (линии, окружности), меняется расположение осей X и Y.

Для работы с SD картой использован модуль FatFs версии R0.09 .

Цветной TFT LCD с контроллером ST7735R, со слотом для MicroSD карты (это клон Adafruit Mini TFT - 0.96" 160x80; https://www.adafruit.com/product/3533 (почему-то только через TOR) ).

datasheet ST7735R v1.4

Весь исходный код. ide CooCox.

connection STM32 and 25LC160

Соединение:
Vin ▶ +3.3V
3V ▶ -
GND ▶ ground
SCK ▶ PB13
MISO ▶ PB14
MOSI ▶ PB15
TFTCS ▶ PC0
RST ▶ PC1
DC ▶ PC2
SDCS ▶ PC6
LIT ▶ PA1

Недостатки:
1. Реализовано мало функций
2. Использование дополнительной оперативной памяти не предполагалось. Своей памяти у stm32F100RBT6 недостаточно, чтобы формировать массив данных для всего экрана сразу. Для данного дисплея нужно 25 КБ (160*80*2), а если использовать 24-битные bmp , то ещё 37,5 КБ (если не объединять). Поэтому скорость вывода bmp(на видео картинки с буквой "Ё" и словом "mirossa") заметно медленнее, чем просто заливка экрана. С доп памятью было бы одинаково.

Ещё:
У этого дисплея начальная координата - (24,0), а не (0,0). Если поменять оси X и Y местами (функции дисплея), то (0,24). Добавил этот нюанс в функции, чтобы на уровне приложения задавать привычные (0,0).

некоторые модули:

/* main.c */
#include <stm32f10x.h>
#include <stm32f10x_rcc.h>
#include <stm32f10x_gpio.h>
#include <stm32f10x_spi.h>
#include "lcd_st7735.h"
#include <string.h>
#include "ff.h"

extern uint8_t ST7735_width_curr;
extern uint8_t ST7735_height_curr;

UINT bw, br;
FIL Fil1;
FILINFO fno;        /* FILINFO structure */
FATFS Fatfs;
FRESULT frez;

// bmp24 b
//  https://studassistent.ru/c/otobrazhenie-kartinki-bmp-c-si
struct
    {
        short signature;
        int size;
        int adress;
        int lengthHead;
        int length;
        int heigh;
        short numColPlosk;
        short bitPerPix;
        int comprMet;
        int arrLength;
        int gorResolution;
        int vertResolution;
        int numColors;
        int numMainColors;
    } headBMP;

char pic[480];      // BGR BGR BGR ...  // 1 line = 160 px * 3 byte; для ширины в 160 пикселей
uint8_t readPic(char *path);
uint8_t writePic(char *path);
void verticalReflectC(void);

uint8_t readAndPrintBMP();
// bmp24 e

void fillScreen(uint16_t color);
void otherInit(void);

//void Delay(uint32_t nTime);
uint8_t debounce(uint8_t last);

#define rlen 80

int main(void)
{
	uint8_t currentButton;
	uint8_t lastButton;
	uint8_t rej = 0;

	char Buff[70];

	otherInit(); // Initialize user button
	ST7735_init();
	ST7735_backLight(1);

	lastButton = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0);
	currentButton = lastButton;

	// Configure SysTick Timer
	//if (SysTick_Config(SystemCoreClock / 1000))
			//while (1);

	//fillScreen(ST7735_GREEN); //медленно
	fillRect(0, 0, ST7735_width_curr, ST7735_height_curr, ST7735_GREEN);

while (1)	{

	currentButton = debounce(lastButton);

	if ( lastButton == Bit_RESET && currentButton == Bit_SET ){

		if ( rej == 0 ){
			rej++;
			//outChar('X', ST7735_YELLOW, ST7735_BLACK);
			printStr("Let's get started. Here are print of string, read of bitmaps from SD card and "
					"display of them, draw primitive figures.", -1, -1, ST7735_YELLOW, ST7735_BLACK);

		}
		else if ( rej == 1 ){
			rej++;

			if( f_mount(0, &Fatfs) == FR_OK) {

				if ( ( frez = f_open (&Fil1 , "1_40x40.bmp", FA_READ) ) == FR_OK) {

					readAndPrintBMP();
					frez = f_close(&Fil1);

				}else{
					printStr("File is not opened for read.", -1, -1, ST7735_MAGENTA, ST7735_WHITE);
				 }

				frez = f_mount(0, NULL);

			} else{
				printStr("Volume was not mounted.", -1, -1, ST7735_MAGENTA, ST7735_WHITE);
			}

		} else if ( rej == 2 ){
			rej++;

			fillRect(0, 0, 159, 79, ST7735_WHITE);
			fillRect(10, 13, 140, 40, ST7735_MAGENTA);
			DrawRectangle(3,30, 80, 75, 2, ST7735_CYAN);
			DrawLine(2,3, 149, 50, ST7735_BLACK);
			DrawLine(2,50, 149, 3, ST7735_BLACK);
			DrawCircleBresenham(80, 40, 35, ST7735_RED);

		}else if ( rej == 3 ){
			rej++;

			if( f_mount(0, &Fatfs) == FR_OK) {    // Register volume work area

				// READ

				if ( ( frez = f_open (&Fil1 , "2.bmp", FA_READ) ) == FR_OK) {

					readAndPrintBMP();
					frez = f_close(&Fil1);

				}else{
					printStr("File is not opened for read.", -1, -1, ST7735_MAGENTA, ST7735_WHITE);
				 }

				// MAKE DIR, SET CURRENT DIR

				frez = f_stat ( "/files", &fno);
				if ( frez == FR_OK ){
					frez = f_chdir("/files");

				}else if ( frez == FR_NO_PATH ){
					frez = f_mkdir("/files");
					if ( frez == FR_OK )
						frez = f_chdir("/files");

				}else{

				}

				// WRITE

				if ( ( frez = f_open (&Fil1 , "HELLO.TXT", FA_WRITE | FA_CREATE_ALWAYS) ) == FR_OK) {
					if ( ( frez = f_write (&Fil1 , "Hello world !\r\n", 14, &bw) ) == FR_OK ){
						frez = f_close (&Fil1);
					}else{
						printStr("Error of recording to file.", -1, -1, ST7735_MAGENTA, ST7735_WHITE);
					}
				}else{
					printStr("File is not opened for write.", -1, -1, ST7735_MAGENTA, ST7735_WHITE);
				 }

			frez = f_mount(0, NULL);

			} else{
				printStr("Volume was not mounted.", -1, -1, ST7735_MAGENTA, ST7735_WHITE);
			}

		} else if ( rej == 4 ){
			rej++;

			ST7735_setMadctl(MADCTLGRAPHICS_normal);
			//fillScreen(ST7735_CYAN); // медленно
			fillRect(0, 0, ST7735_width_curr, ST7735_height_curr, ST7735_CYAN);

			//outChar('O', ST7735_YELLOW, ST7735_BLACK);


		} else {
			rej = 0;

			if( f_mount(0, &Fatfs) == FR_OK) {

				if ( ( frez = f_open (&Fil1 , "1_40x40.bmp", FA_READ) ) == FR_OK) {

					readAndPrintBMP();
					frez = f_close(&Fil1);

				}else{
				 printStr("File is not opened for read.", -1, -1, ST7735_MAGENTA, ST7735_WHITE);
				 }

				frez = f_mount(0, NULL);

			} else{
				printStr("Volume was not mounted.", -1, -1, ST7735_MAGENTA, ST7735_WHITE);
			}

		}
	}

	lastButton = currentButton;

}//while

    return 0;
}

void otherInit(void){
	GPIO_InitTypeDef GPIO_InitStructure;


	// user button A0
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
		GPIO_Init(GPIOA,	&GPIO_InitStructure);
}

/*static __IO uint32_t TimingDelay;

void Delay(uint32_t nTime){
	TimingDelay = nTime;
	while(TimingDelay != 0);
}


void SysTick_Handler(void){
	if (TimingDelay != 0x00)
		TimingDelay--;
}
*/

// обработка дребега контакта кнопки
uint8_t debounce(uint8_t last){
	uint8_t current = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0);
	  if ( last != current ){
		  Delay(5);
		  current = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0);
	  }
  return current;
}

//вместо этой функции(медленная) лучше использовать fillRect()
void fillScreen(uint16_t color){
	uint8_t x, y;
	ST7735_setAddrWindow(0, 0, ST7735_width_curr-1, ST7735_height_curr-1);
	for ( x=0; x < ST7735_width_curr; x++ ) {
		for ( y=0; y < ST7735_height_curr; y++ ) {
			ST7735_pushColor(&color, 1);
		}
	}
}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
	/* Infinite loop */
	/* Use GDB to find out why we're here */
	while (1);
}
#endif

// работа с 24-битным bitmap.
// оперативной памяти под целый bmp файл ( > 38Kb для 160x80 пикселей ) не хватит.
// поэтому из файла (на SD карте) считывается, и выводится на экран, по одной "пиксельной"
// строке за раз. Bitmap выводится в "верхний левый" угол экрана. Если ширина/длина дисплея
// больше чем у bmp, то изображение будет занимать часть экрана.
// Если ширина/длина дисплея меньше, чем размер bmp, то из файла будут считываться
// пиксельные строки по ширине экрана, часть выходящая за экран отбрасывается(не считывается).
// Если по какой-то причине(редкой) из файла будет прочитано меньше, чем запрошено, то строка
// будет выведена не полностью, а управление перейдёт на вывод следующей строки.
uint8_t readAndPrintBMP() {
        long int i, y;
        uint8_t d, n;
        uint16_t cr, bytePerLine, cc;

	frez = f_read (&Fil1 , &headBMP.signature , sizeof(short), &br);
	if (frez || !br){
		printStr("read error.", -1, -1, ST7735_MAGENTA, ST7735_WHITE);
		return 1;
	}
	if(headBMP.signature != (short)0x4d42){
		printStr("File is not bmp.", -1, -1, ST7735_MAGENTA, ST7735_WHITE);
		return 1;
	}
	else
	{
		f_read (&Fil1 , &headBMP.size , sizeof(int), &br);
		f_lseek(&Fil1, f_tell(&Fil1) + 2*sizeof(short));
		f_read (&Fil1 , &headBMP.adress , sizeof(int), &br);
		f_read (&Fil1 , &headBMP.lengthHead , sizeof(int), &br);
		f_read (&Fil1 , &headBMP.length , sizeof(int), &br);
		f_read (&Fil1 , &headBMP.heigh , sizeof(int), &br);
		f_read (&Fil1 , &headBMP.numColPlosk , sizeof(short), &br);
		f_read (&Fil1 , &headBMP.bitPerPix , sizeof(short), &br);
		if (headBMP.bitPerPix != (short)24) {
			printStr("Bmp is not 24.", -1, -1, ST7735_MAGENTA, ST7735_WHITE);
			return 1;
		}
		else {
			f_read (&Fil1 , &headBMP.comprMet , sizeof(int), &br);
			f_read (&Fil1 , &headBMP.arrLength , sizeof(int), &br);
			f_read (&Fil1 , &headBMP.gorResolution , sizeof(int), &br);
			f_read (&Fil1 , &headBMP.vertResolution , sizeof(int), &br);
			f_read (&Fil1 , &headBMP.numColors , sizeof(int), &br);
			f_read (&Fil1 , &headBMP.numMainColors , sizeof(int), &br);
		}

	}

	frez = f_lseek(&Fil1, 54*sizeof(char));

	// что меньше ширина/высота в файле или текущая ширина/высота экрана
	cr = ( headBMP.heigh > ST7735_height_curr) ?  ST7735_height_curr: headBMP.heigh; //row
	cc = ( headBMP.length > ST7735_width_curr) ?  ST7735_width_curr: headBMP.length; //col

	bytePerLine = cc * 3;

	// (командой MADCTL (36h) можно выбрать расшифровку значения цветов - RGB  или BGR. сейчас - RGB)
	// в bmp файле каналы цвета располагаются в GBR порядке.
	// преобразую BGR 24-бит в RGB 16-бит с учётом того, что формирую последовательность
	// байтов цветов в массиве CHAR, а затем функция lcd_printPixelLine работает с ним
	// как с массивом uint16_t (порядок байт не "mb lb", а "lb mb")
	for (i = 0; i < cr; i++){

		frez = f_read (&Fil1, pic , bytePerLine, &br);
		if ( frez != FR_OK)
			break;

		n = br/3; // count of pixels
		for ( y = 0; y < n; y++){

			pic[y*2] = pic[y*3] >> 3; // B сдвигается вправо
			pic[y*2] |= (pic[y*3+1] & 0x1C) << 3; //слева от B добавляюся 3 старших бита G
			pic[y*2+1] = pic[y*3+1] >> 5; // R сдвигается вправо
			pic[y*2+1] |= (pic[y*3+2] & 0xF8) ; //к R присоединяются 3 средних бита G

		}
		// строки в bmp располагаются в обратном порядке в направлении заголовков
		// считываю строки в ЭТОМ порядке, а вывожу снизу вверх.
		lcd_printPixelLine(cr - 1 - i, (uint16_t *)pic, n);

		if ( bytePerLine != br || cc < headBMP.length )
			frez = f_lseek(&Fil1, 54*sizeof(char) + 3*headBMP.length* (i+1) );
	}

	return 0;


}


/* lcd_st7735.h*/

#ifndef _LCD_ST7735_
#define _LCD_ST7735_

//#define MADCTLGRAPHICS 0x6 // X-Mirror Y-Mirror
#define MADCTLGRAPHICS 0x3  // X-Y Exchange X-Mirror
#define MADCTLGRAPHICS_normal 0x0 // normal             10.1.27    9.11.3
#define MADCTLBMP      0x2

// начальная координата смещена. вместо (0,0) брать (24,0)
//#define ST7735_width 80
//#define ST7735_height 160

// Color definitions
#define	ST7735_BLACK   0x0000
#define	ST7735_BLUE    0x001F
#define	ST7735_RED     0xF800
#define	ST7735_GREEN   0x07E0
#define ST7735_CYAN    0x07FF
#define ST7735_MAGENTA 0xF81F
#define ST7735_YELLOW  0xFFE0
#define ST7735_WHITE   0xFFFF
#define ST7735_ORANGE  0xFBE4

//Установка порядка записи (полученных от МК) данных в массив памяти контроллера.
//(Одна и та же последовательность данных, отправляемых из МК на контроллер дисплея,
// будет отображаться по вертикали, по горизонтали, снизу вверх или сверху вниз...)
// для madctl используются: MADCTLGRAPHICS, MADCTLGRAPHICS_normal, MADCTLBMP
void ST7735_setMadctl(uint8_t madctl);

//Установка начальной и конечной координат окна(выбранного участка дисплея), куда будут
//выводиться последующие данные.
void ST7735_setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);

//вывод пикселей цвета color в количестве cnt от текущей позиции в массиве памяти контроллера.
void ST7735_pushColor(uint16_t * color, int cnt);

//Инициализация
void ST7735_init();

// вкл/выкл подсветку дисплея
void ST7735_backLight(uint8_t on);

// сделать задержку nTime мс.
void Delay(uint32_t nTime);

//вывести символ с текущей позиции (после посленего вывода)
void outChar(uint8_t ch, uint16_t foreground, uint16_t background);
//вывести строку с указанной позиции
void printStr(char * str, int16_t X, int16_t Y, uint16_t foreground, uint16_t background);
// заполнить прямоугольную область одним цветом
void fillRect(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint16_t color); // horiz, vert lines
// нарисовать прямоугольник нужной толщины
void DrawRectangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2,  uint8_t thickness, uint16_t color);

// нарисовать линию из точки (x1,y1) в точку (x2, y2)
void DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint16_t color);
// нарисовать линию из точки (x1,y1) в точку (x2, y2) методом Брезенхама
void DrawLineBresenham(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint16_t color);
// нарисовать круг методом Брезенхама
void DrawCircleBresenham(uint8_t x0, uint8_t y0, uint8_t r, uint16_t color);
// вывести линию
void lcd_printPixelLine(uint8_t y, uint16_t * colors, uint8_t n);
#endif


/* spi.h */

#ifndef SPI_H_
#define SPI_H_

enum spiSpeed { SPI_SLOW, SPI_MEDIUM, SPI_FAST };

// Основная настройка SPI. Работает только с SPI2.
void spiInit(SPI_TypeDef* SPIx);

// Передача/получение данных по 8 бит
// SPIx - только SPI2
// rbuf - указатель на буфер, куда будут загружены данные
// tbuf - указатель на отправляемые данные

// cnt - количество (по 8 бит)
//
// Если в tbuf передан указатель, а в rbuf - 0, то будут отправлены данные, а получаемые
//  данные никуда записываться не будут.
// Если наоборот - будут получены данные, отправляться будет "пустой" сигнал (0xff).
// Если оба указателя не нулевые, то данные будут переданы и получены.
int spiReadWrite(SPI_TypeDef* SPIx, uint8_t * rbuf, const uint8_t * tbuf, int cnt, enum spiSpeed speed);

// Передача/получение данных по 16 бит
// см. описание функции spiReadWrite()  (8 бит -> 16 бит)
int spiReadWrite16(SPI_TypeDef* SPIx, uint16_t * rbuf, const uint16_t * tbuf, int cnt, enum spiSpeed speed);

#endif


/* spi_dma.h */

#ifndef SPI_DMA_H_
#define SPI_DMA_H_

#include <stm32f10x.h>

// Инициализация. Включение тактирования
void spi_dma_Init(void);

//Передача/Получение данных по SPI c DMA
//(основные настройки SPI должны быть уже выполнены (spi.h.c) )
// SPIx - SPI1 или SPI2
// half: 0 - 8 бит, 1 - 16 бит
// tbuf - указатель на отправляемые данные
// rbuf - указатель на буфер, куда будут загружены данные
// count - количество порций данных размером по параметру half
//
// добавлил параметр dontIncMem для увеличения скорости заливки ( lcd_st7735.c функция fillRect() )
// - инкрементировать или нет указатель на блок памяти при отправке данных
//   (включается в функциях заливки, где одним цветом заливается участок экрана)
//
// Если в tbuf передан указатель, а в rbuf - 0, то будет запущена фукнция отправки.
// Если наоборот - будет запущена функция получения.
// Если оба указателя не нулевые, то будет запущено копирование из памяти в память
int xchng_datablock(SPI_TypeDef *SPIx , uint8_t half , const void *tbuf , void *rbuf , unsigned count, uint8_t dontIncMem);

#endif

110

Комментарии




information