Китайские клоны STM32

Я не правильно выразился. Там у этой ассемблерной команды задается маска - какие конкретно пины из 8-ми выставить в 1 а какие - в 0.

Но суть моего высказывания вы поняли… Один такт - это только если фиксированная маска

отбой, увидел косяк.

для 411 чипа SPI + DMA функции CMSIS

Спойлер
/*
 * f4spi.h
 *
 *  Created on: Sep 3, 2024
 *      Author: seleznev_a
 *
 *      PA5 SCK SPI1
 *      PA7 MOSI SPI1
 *      PA6 MISO SPI1 - not use
 *
 */

#ifndef F4SPI_H_
#define F4SPI_H_

void f4initSPI1(void); // инициализация шины
void f4writeSPI1(unsigned char * buff, unsigned long buff_size); // отправка буфера в шину
void f4sendSPI1(unsigned char cmd); // отправка одного байта
void f4wordSPI1write(unsigned long buff_size, unsigned short value); // отправка в шину слова (16 бит) в нужном количестве
void f4buffSPI1word(unsigned short * buff, unsigned long buff_size); // отправка буфера из слов (16 бит) в шину

// функции для отправки по DMA // перед использованием или проверить занятость или принудительно остановить
void f4setCallBackEndTXRX(void (*inCallbackTXRX)()); // установка функции, которая вызывается по окончанию передачи
void f4stopSPIDMA(void); // остановка отправки
bool f4busySPIDMA(void); // занятость интерфейса, если необходимо какому либо устройству отправить окончание передачи - используем в loop
void f4wordSPI1writeDMA(unsigned long buff_size, unsigned short value); // отправка в шину слова (16 бит) в нужном количестве через DMA
void f4buffSPI1wordDMA(unsigned short * buff, unsigned long buff_size); // отправка буфера из слов (16 бит) в шину через DMA

#endif /* F4SPI_H_ */




/*
 * f4spi.cpp
 *
 *  Created on: Sep 3, 2024
 *      Author: seleznev_a
 */

#include "f4spi.h"
#include "stm32f4xx.h"
#include "f4base.h"
#include <stdio.h>

void (*callbackSPIendTransferDMA)();
bool inWork;

void f4initSPI1(void) {
	f4stopSPIDMA();
	callbackSPIendTransferDMA = NULL;
	RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; // GPIO port A
	RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; // SPI ON
    // pin SCK PA5 AF5 push-pull
    GPIOA->MODER &= ~GPIO_MODER_MODER5; // 00 — clear bits
    GPIOA->MODER |= GPIO_MODER_MODER5_1; // 10 — режим альтернативной функции.
    GPIOA->MODER &= ~GPIO_MODER_MODER5_0; // 10 — режим альтернативной функции.
    GPIOA->OTYPER &= ~GPIO_OTYPER_OT_5; // 0 - двухтактный выход или push-pull сокращено PP (после сброса)
    GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR5; // 11 — 50 MHz
    GPIOA->AFR[0] |=(0x05 << 20); // Назначаем PA5 выводу альтернативную функцию AF5
    // pin MISO PA6 input pull-up
    GPIOA->MODER &= ~GPIO_MODER_MODER6; // 00 — clear bits
    GPIOA->MODER |= GPIO_MODER_MODER6_1; // 10 — режим альтернативной функции.
    GPIOA->MODER &= ~GPIO_MODER_MODER6_0; // 10 — режим альтернативной функции.
    GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR6; // 00 — reset bits
    GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR6_1; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
    GPIOA->PUPDR |= GPIO_PUPDR_PUPDR6_0; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
    GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR6; // 11 — 50 MHz
    GPIOA->AFR[0] |=(0x05 << 24); // Назначаем PA6 выводу альтернативную функцию AF5
    // pin MOSI PA7 AF5 push-pull
    GPIOA->MODER |= GPIO_MODER_MODER7_1; // 10 — режим альтернативной функции.
    GPIOA->MODER &= ~GPIO_MODER_MODER7_0; // 10 — режим альтернативной функции.
    GPIOA->OTYPER &= ~GPIO_OTYPER_OT_7; // 0 - двухтактный выход или push-pull сокращено PP (после сброса)
    GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR7; // 11 — 50 MHz
    GPIOA->AFR[0] |=(0x05 << 28); // Назначаем PA7 выводу альтернативную функцию AF5
    // init SPI1
    SPI1->CR1 = 0;
    SPI1->CR2 = 0;
    //SPI1->CR1 &= ~SPI_CR1_BR; // 000: fPCLK/2
    //SPI1->CR1 &= ~SPI_CR1_CPOL; // Clock polarity 0/1
    //SPI1->CR1 &= ~SPI_CR1_CPHA; // Clock phase // 0: The first clock transition is the first data capture edge // The second clock transition is the first data capture edge
    SPI1->CR1 &= ~SPI_CR1_DFF; // 0: 8-bit data frame format is selected for transmission/reception
    //SPI1->CR1 &= ~SPI_CR1_LSBFIRST; // 0 - MSB передается первым
    SPI1->CR1 |= SPI_CR1_SSM; // Программное управление SS
    SPI1->CR1 |= SPI_CR1_SSI; // I/O value of the NSS pin is ignored
    SPI1->CR1 |= SPI_CR1_MSTR; // master mode
    SPI1->CR1 |= SPI_CR1_SPE; // включить SPI
    // включить тактирование DMA
    RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
    DMA2_Stream2->CR &= ~DMA_SxCR_CHSEL; // 000: channel 0 selected
    DMA2_Stream2->CR |= DMA_SxCR_CHSEL_1; // 010: channel 2 selected // SPI1 TX
}

void f4sendSPI1(unsigned char cmd) {
    *((__IO uint8_t *)&SPI1->DR) = cmd;
	while ((!(SPI1->SR & SPI_SR_TXE)) && (SPI1->SR & SPI_SR_BSY));
}

void f4writeSPI1(unsigned char * buff, unsigned long buff_size) {
	for(unsigned long i = 0; i < buff_size; ++i) {
	    while(!(SPI1->SR & SPI_SR_TXE)); // do not remark - its not good
	    *((__IO uint8_t *)&SPI1->DR) = *(buff++);
	}
	while ((!(SPI1->SR & SPI_SR_TXE)) && (SPI1->SR & SPI_SR_BSY));
}

void f4wordSPI1write(unsigned long buff_size, unsigned short value) {
    // 16 bit transfer mode
	SPI1->CR1 |= SPI_CR1_DFF;
	while (buff_size--) {
		while (!(SPI1->SR & SPI_SR_TXE)); // do not remark - its not good
		*(volatile uint16_t *)&(SPI1->DR) = value;
    }
    // wait end transfer
	while (!(SPI1->SR & SPI_SR_TXE) || (SPI1->SR & SPI_SR_BSY));
    // return 8bit transfer mode
	SPI1->CR1 &= ~SPI_CR1_DFF;
}

void f4buffSPI1word(unsigned short * buff, unsigned long buff_size) {
    // 16 bit transfer mode
	SPI1->CR1 |= SPI_CR1_DFF;
	while (buff_size--) {
		while (!(SPI1->SR & SPI_SR_TXE)); // do not remark - its not good
		*(volatile uint16_t *)&(SPI1->DR) = *(buff++);
    }
    // wait end transfer
	while (!(SPI1->SR & SPI_SR_TXE) || (SPI1->SR & SPI_SR_BSY));
    // return 8bit transfer mode
	SPI1->CR1 &= ~SPI_CR1_DFF;
}

void f4setCallBackEndTXRX(void (*inCallbackTXRX)()) {
	callbackSPIendTransferDMA = inCallbackTXRX;
}

void f4stopSPIDMA(void) {
	inWork = false; // сбрасываем флаг работы
	DMA2_Stream2->CR &= ~DMA_SxCR_EN; // отключаем DMA
	SPI1->CR2 &= ~SPI_CR2_TXDMAEN; // у SPI отключаем TX DMA
	while(DMA2_Stream2->CR & DMA_SxCR_EN); // ждем как DMA отключится
	DMA2->LIFCR |= DMA_LIFCR_CTCIF2; // сброс флага DAMA Transfer Complete
	__attribute__((unused)) unsigned long getDr = SPI1->DR; // очитка регистра данных шины SPI
	if (callbackSPIendTransferDMA != nullptr) { // если прописана функция остановки передачи/приема при окончании
		callbackSPIendTransferDMA(); // вызываем ее
	}
    // return 8bit transfer mode
	SPI1->CR1 &= ~SPI_CR1_DFF;
}

bool f4busySPIDMA(void) {
	if (inWork) {
		if ((!(SPI1->SR & SPI_SR_BSY)) && (SPI1->SR & SPI_SR_TXE) && (DMA2->LISR & DMA_LISR_TCIF2)) { // проверка занятости потока DMA и интерфейса SPI
			f4stopSPIDMA();
		} else {
			return true;
		}
	}
	return false;
}

void f4wordSPI1writeDMA(unsigned long buff_size, unsigned short value) {
	static unsigned short data_colors;
    // 16 bit transfer mode
	SPI1->CR1 |= SPI_CR1_DFF;
	// send by DMA
	data_colors = value;
	inWork = 1; // флаг начала работы
	DMA2_Stream2->PAR = (unsigned long)(&SPI1->DR); //заносим адрес регистра DR в CPAR
	DMA2_Stream2->M0AR = (unsigned long)((unsigned char *)(&data_colors)); //заносим адрес данных в регистр CMAR
	DMA2_Stream2->M1AR = (unsigned long)((unsigned char *)(&data_colors)); //заносим адрес данных в регистр CMAR
	DMA2_Stream2->NDTR = buff_size; // передаем нужное количество
	DMA2_Stream2->CR &= ~DMA_SxCR_PL; //приоритет низкий
	DMA2_Stream2->CR |= DMA_SxCR_PL_1; // Priority level // 10: High
	DMA2_Stream2->CR &= ~DMA_SxCR_MSIZE; // Memory data size // 00: byte (8-bit)
	DMA2_Stream2->CR |= DMA_SxCR_MSIZE_0; // Memory data size // 01: half-word (16-bit)
	DMA2_Stream2->CR &= ~DMA_SxCR_PSIZE; // Peripheral data size
	DMA2_Stream2->CR |= DMA_SxCR_PSIZE_0; // Peripheral data size // 01: Half-word (16-bit)
	DMA2_Stream2->CR &= ~DMA_SxCR_MINC; // 0: Memory address pointer is fixed
	DMA2_Stream2->CR &= ~DMA_SxCR_PINC; // 0: Peripheral address pointer is fixed
	DMA2_Stream2->CR &= ~DMA_SxCR_CIRC; // 0: Circular mode disabled
	DMA2_Stream2->CR &= ~DMA_SxCR_DIR; // 00: Peripheral-to-memory
	DMA2_Stream2->CR |= DMA_SxCR_DIR_0; // 01: Memory-to-peripheral
	SPI1->CR2 |= SPI_CR2_TXDMAEN; // у SPI включаем TX DMA
	DMA2_Stream2->CR |= DMA_SxCR_EN; // запускаем DMA
}

void f4buffSPI1wordDMA(unsigned short * buff, unsigned long buff_size) {
    // 16 bit transfer mode
	SPI1->CR1 |= SPI_CR1_DFF;
	// send by DMA
	inWork = 1; // флаг начала работы
	DMA2_Stream2->PAR = (unsigned long)(&SPI1->DR); //заносим адрес регистра DR в CPAR
	DMA2_Stream2->M0AR = (unsigned long)((unsigned short *)(buff)); //заносим адрес данных в регистр CMAR
	DMA2_Stream2->NDTR = buff_size; // передаем нужное количество
	DMA2_Stream2->CR &= ~DMA_SxCR_PL; //приоритет низкий
	DMA2_Stream2->CR |= DMA_SxCR_PL_1; // Priority level // 10: High
	DMA2_Stream2->CR &= ~DMA_SxCR_MSIZE; // Memory data size // 00: byte (8-bit)
	DMA2_Stream2->CR |= DMA_SxCR_MSIZE_0; // Memory data size // 01: half-word (16-bit)
	DMA2_Stream2->CR &= ~DMA_SxCR_PSIZE; // Peripheral data size
	DMA2_Stream2->CR |= DMA_SxCR_PSIZE_0; // Peripheral data size // 01: Half-word (16-bit)
	DMA2_Stream2->CR |= DMA_SxCR_MINC; // 1: Memory address pointer is incremented after each data transfer (increment is done according to MSIZE)
	DMA2_Stream2->CR &= ~DMA_SxCR_PINC; // 0: Peripheral address pointer is fixed
	DMA2_Stream2->CR &= ~DMA_SxCR_CIRC; // 0: Circular mode disabled
	DMA2_Stream2->CR &= ~DMA_SxCR_DIR; // 00: Peripheral-to-memory
	DMA2_Stream2->CR |= DMA_SxCR_DIR_0; // 01: Memory-to-peripheral
	SPI1->CR2 |= SPI_CR2_TXDMAEN; // у SPI включаем TX DMA
	DMA2_Stream2->CR |= DMA_SxCR_EN; // запускаем DMA
}