9. DMA&USART&I2C&SPI (STM32)

1. DMA direct memory access-DMA transfer data/DMA + AD multi-channel

http://t.csdnimg.cn/fauUzhttp://t.csdnimg.cn/fauUz

The following is my own learning version code, read more comments, they are all the essence.

Understand the DMA library functions

Understanding DMA library functions
void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx);//Restore default configuration
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);//Initialization
void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct);//Structure initialization
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);//Enable
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);//Interrupt output enable
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber); //DMA sets the current data register (writes data to the transmission counter)
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);//DMA gets the current data register (returns the value of the transmission timer, you can see how much data has not been transferred)
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);//Get the flag status
void DMA_ClearFlag(uint32_t DMAy_FLAG);//Clear the flag bit
ITStatus DMA_GetITStatus(uint32_t DMAy_IT);//Get the interrupt status
void DMA_ClearITPendingBit(uint32_t DMAy_IT);//Clear the interrupt pending bit

1.1, DMA data transfer

MyDMA.c

#include "stm32f10x.h" // Device header
//Initialize DMA
uint16_t MyDMA_Size;

//First step, RCC turns on the DMA clock
//The second step, directly call DMA_Init(), initialize each parameter (including the starting address of the peripheral and memory site, data width, whether the address is incremented, direction, transmission timer, whether automatic reloading is required, and select the trigger source M2M, there is also a channel priority (not shown in the picture))

//The third step is to switch control, DMA_Cmd, to enable the specified channel.
//Here, if you choose hardware trigger, don't forget to call XXX_DMACmd on the corresponding peripheral to turn on the output of the trigger signal
//If you need DMA interrupts, call DMA_ITConfig, turn on the interrupt output, and then configure the corresponding interrupt channel in the NVIC and write the interrupt function (there is no configuration diagram for the interrupt, it is the same for each peripheral, just refer to other diagrams)
//The fourth step, during the running process, if the transfer is completed and the transmission timer is cleared, if you want to assign a value to the transmission timer again, disable DMA, write the transmission timer, and enable DMA, like this Just fine

//Understanding DMA library functions
//void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx); //Restore default configuration
//void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);//Initialization
//void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct); //Structure initialization
//void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState); //Enable
//void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);//Interrupt output enable
//void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber); //DMA sets the current data register (writes data to the transmission counter)
//uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);//DMA gets the current data register (returns the value of the transmission timer, you can see how much data has not been transferred)
//FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);//Get the flag status
//void DMA_ClearFlag(uint32_t DMAy_FLAG); //Clear the flag bit
//ITStatus DMA_GetITStatus(uint32_t DMAy_IT);//Get the interrupt status
//void DMA_ClearITPendingBit(uint32_t DMAy_IT);//Clear the interrupt pending bit



void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{
MyDMA_Size = Size;//Let Size pass a copy to the global variable MyDMA_Size, and then use it in subsequent functions
\t
//Turn on the clock, the device on the AHB bus during DMA, use the function of turning on the clock with AHB
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
//The second step, directly call DMA_Init(), initialize each parameter (including the starting address of the peripheral and memory site, data width, whether the address is incremented, direction, transmission timer, whether automatic reloading is required, and select the trigger source M2M, there is also a channel priority (not shown in the picture))
\t
//Three parameters of peripheral site
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;//Starting address of the peripheral site (base address)
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//Data width of peripheral site: select byte
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;//Whether the peripheral site is auto-incremented: The transfer in the middle of this array must be auto-incremented, otherwise the data will be overwritten, so choose auto-increment

//The three parameters of the memory site are the same.
DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;//Starting address of the memory site
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //Data width of memory site
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//Whether the memory increases automatically

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //Transmission direction: DST peripheral site as the destination, the transmission direction is memory to peripheral site | SRC peripheral site as the source, that is, the transmission direction is the transmission direction from the peripheral site to the memory site, Choose SRC
DMA_InitStructure.DMA_BufferSize = Size;//The size of the buffer (transmission counter) specifies the size of the buffer area in data units. This data unit = peripheral data width or memory data width, depending on the transmission direction
//Specify the buffer size in data units, which means you want to transmit several data units. This data unit is equal to the DataSize of your transmission source site.
//To put it simply, BufferSize is the transmission timer, specifying how many times to transmit.
//In the source code of the DMA_Init function, the BufferSize parameter is actually directly assigned to the register of the transmission counter, with values ranging from 0 to 65535.
//Extract BufferSize into the parameter of the function, that is, uint16_t Size, and then put Size into the BufferSize parameter
\t
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //Transfer mode (whether to use automatic reloading) (Note: Automatic reloading cannot be applied in the case of memory to memory, that is, loop mode and software trigger mode cannot be used at the same time. If used at the same time, DMA It will trigger continuously and never stop)
//There are two parameter options: Circular, the transmission counter is automatically reloaded; Normal, the transmission counter is not reloaded in normal mode, and stops after it decreases to 0. Memory is transferred to memory, and the transfer can be stopped after one transfer.
\t\t\t\t\t\t\t\t\t\t\t\t 
DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;//M2M selects whether it is memory to memory (in fact, it is to select hardware trigger or software trigger)
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//Priority
\t
//The first parameter can select channels 1 to 7 of DMA1, or channels 1 to 5 of DMA2
//Because this is a memory-to-memory transfer, software trigger is used, so any channel of DMA can be used
DMA_Init(DMA1_Channel1, & DMA_InitStructure);
\t
DMA_Cmd(DMA1_Channel1, DISABLE);//Do not allow DMA to be transferred immediately after initialization, but wait until the subsequent function is called before transferring
}

//In the MyDMA_Init() function:
//DMA_InitStructure.DMA_PeripheralBaseAddr = ; The parameter must be written with a 32-bit address, such as an address such as 0x20000000.
//But for SRAM arrays, their addresses are assigned by the compiler and are not fixed, so the absolute address is generally not written, but the address is obtained through the array name.
//This address can be extracted as a parameter of the initialization function. In this way, whichever array you want to transfer, just pass in the address of which array.
//So the parameter void in MyDMA_Init(void) is changed to uint32_t AddrA, and then AddrA is placed in the peripheral base address parameter position
//This completes the address of the peripheral site


//There are three conditions for DMA work: 1. The transmission counter is greater than 0; the second condition: the trigger source has a trigger signal; the third condition, DMA is enabled


//DMA transfer function
void MyDMA_Transfer(void)
{
//Here you need to re-assign the transmission counter. When assigning a value to the transmission counter, you must first disable DMA.
//Three conditions for DMA working
DMA_Cmd(DMA1_Channel1, DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);
DMA_Cmd(DMA1_Channel1, ENABLE);//Call once, transfer once
\t
//Wait for the transfer to complete. Transfer also takes time. Use the check flag function and add a while loop (this was also used before)
while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);//After the transfer is completed, the flag position is 1, the transfer is completed, and the flag position is cleared manually below
DMA_ClearFlag(DMA1_FLAG_TC1);//Clear the flag bit
}

MyDMA.h

#ifndef __MYDMA_H
#define __MYDMA_H

void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size);
void MyDMA_Transfer(void);

#endif

main.c

#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"
//The task of this code: initialize DMA and let DMA transfer the data of DataA to DataB
uint8_t DataA[] = {0x01, 0x02, 0x03, 0x04};//source array
uint8_t DataB[] = {0, 0, 0, 0};//Target array
//uint8_t DataB = 0x66;
int main(void)
{
OLED_Init();
//Transmit the address of the array to it (the array name is the address, no need to add address symbols)
MyDMA_Init((uint32_t)DataA, (uint32_t)DataB, 4);//There are four data, so the number of data transmission times is 4
\t
OLED_ShowString(1, 1, "DataA");
OLED_ShowString(3, 1, "DataB");
OLED_ShowHexNum(1, 8, (uint32_t)DataA, 8);
OLED_ShowHexNum(3, 8, (uint32_t)DataB, 8);
OLED_ShowHexNum(4, 8, (uint32_t) & amp;DataB, 8);//Get address
while (1)
{
DataA[0] + + ;
DataA[1] + + ;
DataA[2] + + ;
DataA[3] + + ;
\t\t
OLED_ShowHexNum(2, 1, DataA[0], 2);
OLED_ShowHexNum(2, 4, DataA[1], 2);
OLED_ShowHexNum(2, 7, DataA[2], 2);
OLED_ShowHexNum(2, 10, DataA[3], 2);
OLED_ShowHexNum(4, 1, DataB[0], 2);
OLED_ShowHexNum(4, 4, DataB[1], 2);
OLED_ShowHexNum(4, 7, DataB[2], 2);
OLED_ShowHexNum(4, 10, DataB[3], 2);
\t\t
Delay_ms(1000);
\t\t
MyDMA_Transfer();
\t\t
OLED_ShowHexNum(2, 1, DataA[0], 2);
OLED_ShowHexNum(2, 4, DataA[1], 2);
OLED_ShowHexNum(2, 7, DataA[2], 2);
OLED_ShowHexNum(2, 10, DataA[3], 2);
OLED_ShowHexNum(4, 1, DataB[0], 2);
OLED_ShowHexNum(4, 4, DataB[1], 2);
OLED_ShowHexNum(4, 7, DataB[2], 2);
OLED_ShowHexNum(4, 10, DataB[3], 2);

Delay_ms(1000);
}
}

1.2, DMA + AD multi-channel

Program execution flow chart:

5c04228aee074208a05d07c09ed4df48.png

AD.c

#include "stm32f10x.h" // Device header

uint16_t AD_Value[4];

void AD_Init(void)
{
//Turn on the clocks of ADC1, GPIOA, and DMA1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
\t
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
\t
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, & amp;GPIO_InitStructure);
\t
//ADC orders four "dishes"
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5);
\t
\t
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //Scan mode
ADC_InitStructure.ADC_NbrOfChannel = 4;//Order four dishes and scan four
ADC_Init(ADC1, & amp;ADC_InitStructure);
\t
//The ADC chef has already prepared the "dish". The DMA must bring the "dish" out as soon as possible to prevent it from being covered.
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) & amp;ADC1->DR;//The chef prepares the dish and puts it in the ADC_DR register, so fill in the source address of the dish here, fill in ADC_DR, get the address of ADC->DR, and then force Type conversion to the required uint32_t data type (similar to 4001244C)
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//To transfer data below 16 bits, use half bytes
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//No auto-increment, always transfer data at the same location
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value; // (forced type conversion of the address of the AD_Value array)
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 4;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//Do not use software trigger, trigger source bit ADC1
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_Init(DMA1_Channel1, & DMA_InitStructure);
\t
DMA_Cmd(DMA1_Channel1, ENABLE);
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
\t
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1) == SET);
\t
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}

AD.h

#ifndef __AD_H
#define __AD_H

extern uint16_t AD_Value[4];//extern represents a callable array

void AD_Init(void);

#endif

main.c

#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

int main(void)
{
OLED_Init();
AD_Init();
\t
OLED_ShowString(1, 1, "AD0:");
OLED_ShowString(2, 1, "AD1:");
OLED_ShowString(3, 1, "AD2:");
OLED_ShowString(4, 1, "AD3:");
\t
while (1)
{
OLED_ShowNum(1, 5, AD_Value[0], 4);
OLED_ShowNum(2, 5, AD_Value[1], 4);
OLED_ShowNum(3, 5, AD_Value[2], 4);
OLED_ShowNum(4, 5, AD_Value[3], 4);
\t\t
Delay_ms(100);
}
}

2. Serial port

2.1, USART serial port protocol, serial port peripherals-data sending/data sending + receiving

http://t.csdnimg.cn/QFLmLhttp://t.csdnimg.cn/QFLmL

2.2. Serial port sends and receives data packets-serial port sends and receives HEX data packets/serial port sends and receives text data packets

http://t.csdnimg.cn/fKyvxhttp://t.csdnimg.cn/fKyvx

3, I2C

3.1, I2C communication protocol-software I2C reading and writing MPU6050

http://t.csdnimg.cn/jnGsVhttp://t.csdnimg.cn/jnGsV

3.2, I2C peripheral bus-hardware I2C read and write MPU6050

http://t.csdnimg.cn/uz2FPhttp://t.csdnimg.cn/uz2FP

4.SPI

4.1. Introduction to SPI communication protocol/W25Q64 – software SPI reading and writing W25Q64

http://t.csdnimg.cn/7Fmnzhttp://t.csdnimg.cn/7Fmnz

4.2, SPI communication peripherals

http://t.csdnimg.cn/PazYxhttp://t.csdnimg.cn/PazYx