DMA data transfer & DMA+AD multi-channel—-DMA direct memory access

Define a variable on the OLED display and see that it starts with 0x2000 on the SRAM; add const before the variable to make it a constant and then check the address that starts with 0x8000 on the Flash, if there is a piece of data that does not need to be changed Large content can be stored in Flash to save SRAM space.

For peripheral registers, their addresses are fixed: first check the starting address of the peripheral where the register is located (Chapter 2 Memory Map), and then check the offset in the peripheral register table. The result is the start address + offset.

Define a peripheral structure. If the starting address of this structure is specified as the starting address of the ADC1 peripheral register, then the memory of the structure will be perfectly mapped to the memory of the peripheral register, so accessing a member variable of the structure is equivalent to accessing a certain register of this peripheral

ADC1->DR ADC1 is a structure pointer, which points to the start address of ADC1 peripherals. Accessing structure members is equivalent to adding an offset, and the start address plus offset is the specified register.

DMA data transfer

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

void MyDMA_Init(uint32_t ADDRA, uint32_t ADDRB, uint16_t size)
{
MyDMA_Size=size;
//DMA is a device of the AHB bus, so use AHB to open the clock function.
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
\t
DMA_InitTypeDef DMA_InitStruct;
DMA_InitStruct.DMA_PeripheralBaseAddr=ADDRA;
DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
//Peripheral self-increment
DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Enable;
DMA_InitStruct.DMA_MemoryBaseAddr=ADDRB;
DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
//Memory auto-increment
DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_BufferSize=size;
DMA_InitStruct.DMA_DIR=DMA_DIR_PeripheralSRC;
//software trigger
DMA_InitStruct.DMA_M2M=DMA_M2M_Enable;
DMA_InitStruct.DMA_Mode=DMA_Mode_Normal;
DMA_InitStruct.DMA_Priority=DMA_Priority_Medium;
\t
DMA_Init(DMA1_Channel1, & DMA_InitStruct);
    DMA_Cmd(DMA1_Channel1, ENABLE);
DMA_Cmd(DMA1_Channel1, DISABLE);
}
//The above code is transferred immediately after initialization, and the DMA stops after one transfer.
//Call this function once to start a DMA transfer
void MyDMA_Transfer(void)
{
//Set the transmission counter value, first disable and then enable
DMA_Cmd(DMA1_Channel1, DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);
DMA_Cmd(DMA1_Channel1, ENABLE);
//Wait for transfer to complete
while(DMA_GetFlagStatus(DMA1_FLAG_TC1)==RESET);
//clear flag
DMA_ClearFlag(DMA1_FLAG_TC1);
\t
}

main function

uint8_t DataA[]={0x01,0x02,0x03,0x04};
uint8_t DataB[]={0,0,0,0};

int main(void)
{
OLED_Init();
\t
MyDMA_Init((uint32_t)DataA,(uint32_t)DataB,4);
OLED_ShowString(1,1,"DataA");
OLED_ShowString(3,1,"DataB");
\t
OLED_ShowHexNum(1,8,(uint32_t)DataA,8);
OLED_ShowHexNum(3,8,(uint32_t)DataB,8);

while(1)
{

DataA[0]++;
DataA[1] + + ;
DataA[2] + + ;
DataA[3] + + ;
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);
//In the while, manually call the transfer, call the transfer once
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);
\t\t
}
}

DMA + AD multi-channel

What is commented out is the one-shot mode

#include "stm32f10x.h" // Device header

uint16_t AD_Value[4];
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
\t
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

RCC_ADCCLKConfig(RCC_PCLK2_Div6); //12MHZ
\t
GPIO_InitTypeDef GPIO_Init_Structure;
//Configure as analog input mode
GPIO_Init_Structure.GPIO_Mode=GPIO_Mode_AIN;
GPIO_Init_Structure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
GPIO_Init_Structure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA, & GPIO_Init_Structure);
\t
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5); //55.5 + 12.5=68
ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_55Cycles5); //55.5 + 12.5=68
ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_55Cycles5); //55.5 + 12.5=68
ADC_RegularChannelConfig(ADC1,ADC_Channel_3,4,ADC_SampleTime_55Cycles5); //55.5 + 12.5=68

ADC_InitTypeDef ADC_InitStruct;
// ADC_InitStruct.ADC_ContinuousConvMode=DISABLE; //Single mode
ADC_InitStruct.ADC_ContinuousConvMode=ENABLE; //Continuous mode whether to scan continuously

ADC_InitStruct.ADC_ScanConvMode=ENABLE;
ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right;
ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
ADC_InitStruct.ADC_Mode=ADC_Mode_Independent;
ADC_InitStruct.ADC_NbrOfChannel=4; //four positions
ADC_Init(ADC1, & ADC_InitStruct);
\t
DMA_InitTypeDef DMA_InitStruct;
DMA_InitStruct.DMA_PeripheralBaseAddr=(uint32_t) & amp;ADC1->DR; //4001244C
DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord; //Low 16-bit ADC1, high 16ADC2
DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable;// Whether the peripheral is self-increasing
DMA_InitStruct.DMA_MemoryBaseAddr=(uint32_t)AD_Value;
DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;
DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable;//
DMA_InitStruct.DMA_BufferSize=4;
DMA_InitStruct.DMA_DIR=DMA_DIR_PeripheralSRC;
DMA_InitStruct.DMA_M2M=DMA_M2M_Disable; //Do not use software trigger
// DMA_InitStruct.DMA_Mode=DMA_Mode_Normal;
DMA_InitStruct.DMA_Mode=DMA_Mode_Circular;//circular mode: transfer counter is automatically reloaded
DMA_InitStruct.DMA_Priority=DMA_Priority_Medium;
//The hardware trigger of ADC1 is only connected to channel 1 of DMA, see the DMA request map
DMA_Init(DMA1_Channel1, & DMA_InitStruct);
\t
DMA_Cmd(DMA1_Channel1, ENABLE);
\t
//Enable the output from ADC to DMA, that is, the peripheral request signal in the DMA request image diagram
ADC_DMACmd(ADC1, ENABLE);

//Enable it
ADC_Cmd(ADC1, ENABLE);
\t
//reset calibration
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1) == SET); //This bit is set by software and cleared by hardware. This bit will be cleared after the calibration register is initialized
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1) == SET);
\t
ADC_SoftwareStartConvCmd(ADC1,ENABLE);//Continuous mode: put the ADC trigger directly in the last line of initialization, AD_GetValue does not need
}
/**
  * @brief ADC starts to convert, scans 4 channels continuously, and DMA also transfers synchronously
  * @param
  * @retval
  */
//void AD_GetValue(void)
//{
// //Single-shot mode requires software to trigger ADC to start
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
//
// DMA_Cmd(DMA1_Channel1,DISABLE);
// DMA_SetCurrDataCounter(DMA1_Channel1,4);
// DMA_Cmd(DMA1_Channel1, ENABLE);

// //transport after conversion
// while(DMA_GetFlagStatus(DMA1_FLAG_TC1)==RESET);
// DMA_ClearFlag(DMA1_FLAG_TC1);

//}

The main function displays the sensor changes

int main(void)
{
OLED_Init();
AD_Init();
OLED_ShowString(1,1,"AD0:");//Potentiometer
OLED_ShowString(2,1,"AD1:");//Infrared sensor
OLED_ShowString(3,1,"AD2:");//Temperature sensing
OLED_ShowString(4,1,"AD3:");//through-beam infrared sensor

while(1)
{
\t\t
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(1000);
}
}