视频链接:[1-1] 课程简介_哔哩哔哩_bilibili
STM32F103c8t6引脚定义表:(橙色是电源相关的引脚,蓝色是最小系统相关的引脚。绿色的是IO口和功能引脚,FT意为能容忍5V电压,主功能是上电默认的功能,默认复用功能是IO口上连接的外设功能引脚,加粗的引脚是能直接用的,没加粗的引脚可能要进行其他配置才能使用)
8-1 DMA直接存储器取存
DMA库函数:
//恢复初始化DMA
void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx);
//初始化DMA
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);
//DMA结构体赋默认值
void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct);
//DMA是否启动
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);
//DMA中断配置
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);
//DMA设置当前数据寄存器,即设置传输计数器
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber);
//DMA获取当前数据寄存器
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
//获取标志位
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);
//清除标志位
void DMA_ClearFlag(uint32_t DMAy_FLAG);
//获取中断标志位
ITStatus DMA_GetITStatus(uint32_t DMAy_IT);
//清除中断标志位
void DMA_ClearITPendingBit(uint32_t DMAy_IT);
8-2 DMA数据转运&DMA+AD多通道
配置DMA的步骤:
1.启动RCC时钟
2.调用DMA初始化函数DMA_Init,配置参数
3.开启DMA,调用DMA_Cmd
4.(如果要硬件触发,要调用XXX_MDACmd,开启触发信号的输出)
5.(如果要DMA中断,调用DMA_ITConfig,开启中断输出)
main.c
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "task01.h"
#include "task02.h"
#include "delay.h"
//任务1 软件触发DMA转运数据
uint8_t DataA[] = {0x01,0x02,0x03,0x04};
uint8_t DataB[] = {00,00,00,00};
void Task01()
{
OLED_Init();
Task01_Init((uint32_t)DataA,(uint32_t)DataB,4);
OLED_ShowHexNum(1,1,DataA[0],2);
OLED_ShowHexNum(1,5,DataA[1],2);
OLED_ShowHexNum(1,9,DataA[2],2);
OLED_ShowHexNum(1,14,DataA[3],2);
while(1)
{
OLED_ShowHexNum(4,1,DataB[0],2);
OLED_ShowHexNum(4,5,DataB[1],2);
OLED_ShowHexNum(4,9,DataB[2],2);
OLED_ShowHexNum(4,14,DataB[3],2);
for(uint16_t i = 0;i < 4;i++)
{
DataA[i] += 0x02;
}
Task01_DMA_Transfer();
Delay_ms(100);
}
}
//ADC作为外设多通道DMA运输
//PA0接电位器 PA1接光温传感器 PA2接温度传感器 PA3接红外传感器
void Task02()
{
OLED_Init();
Task02_Init();
while(1)
{
Task02_AD_GetValue();
// AD_Value[0] = Task02_AD_GetValue(ADC_Channel_0);
// AD_Value[1] = Task02_AD_GetValue(ADC_Channel_1);
// AD_Value[2] = Task02_AD_GetValue(ADC_Channel_2);
// AD_Value[3] = Task02_AD_GetValue(ADC_Channel_3);
// Task02_DMA_Transfer();
OLED_ShowNum(1,1,AD_Value[0],4);
OLED_ShowNum(1,6,AD_Value[1],4);
OLED_ShowNum(2,1,AD_Value[2],4);
OLED_ShowNum(2,6,AD_Value[3],4);
// OLED_ShowNum(3,1,Value[0],4);
// OLED_ShowNum(3,6,Value[1],4);
// OLED_ShowNum(4,1,Value[2],4);
// OLED_ShowNum(4,6,Value[3],4);
Delay_ms(300);
}
}
int main(void)
{
// Task02();
while (1)
{
}
}
task01.c
#include "task01.h"
uint16_t My_Size;
void Task01_Init(uint32_t ADDrA,uint32_t ADDrB,uint16_t Size)//数据从ADDrA->ADDrB,A是外设,B是存储器
{
My_Size = Size;
//初始化DMA
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);//开启RCC时钟
DMA_Cmd(DMA1_Channel1,DISABLE);//先失能DMA
DMA_InitTypeDef My_DMA_InitStructure;
My_DMA_InitStructure.DMA_BufferSize = Size;//缓存区大小,即传输计数器 0~65535
My_DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//传输方向
//DST是指外设站点作为目的地,SRC是指外设站点作为源头
My_DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;//是否使用存储器到存储器,即选择软件触发还是硬件触发
My_DMA_InitStructure.DMA_MemoryBaseAddr = ADDrB;//存储器站点的起始地址
My_DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//存储器站点的数据宽度
My_DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器站点地址是否递增
My_DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//传输模式,选择是否使用自动重装
//DMA的软件触发和自动重装不能同时启用,否则无法停下
My_DMA_InitStructure.DMA_PeripheralBaseAddr = ADDrA;//外设站点的起始地址
My_DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设站点的数据宽度
My_DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;//外设外设站点地址是否递增。
My_DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//优先级配置。
DMA_Init(DMA1_Channel1,&My_DMA_InitStructure);
DMA_Cmd(DMA1_Channel1,ENABLE);
}
void Task01_DMA_Transfer(void)
{
DMA_Cmd(DMA1_Channel1,DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel1,My_Size);
DMA_Cmd(DMA1_Channel1,ENABLE);
//等待转运完成
while(DMA_GetFlagStatus(DMA1_FLAG_GL1) == RESET);
DMA_ClearFlag(DMA1_FLAG_GL1);
}
task02.c
#include "task02.h"
uint16_t AD_Value[4];
void Task02_Init()
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);//开启DMA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);//开启ADC1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GPIOA时钟
//配置ADC
GPIO_InitTypeDef My_GPIO_InitStructure;
My_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 ;
My_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
My_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&My_GPIO_InitStructure); //配置GPIO
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //ADC预分频
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);
ADC_InitTypeDef My_ADC_InitStructure;
My_ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//是否连续转换模式
My_ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//数据右对齐
My_ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//不接触发源
My_ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC为独立模式
My_ADC_InitStructure.ADC_NbrOfChannel = 4;
My_ADC_InitStructure.ADC_ScanConvMode = ENABLE;//配置多通道模式
ADC_Init(ADC1,&My_ADC_InitStructure);//初始化ADC
DMA_Cmd(DMA1_Channel1,DISABLE);//先失能DMA
DMA_InitTypeDef My_DMA_InitStructure;
My_DMA_InitStructure.DMA_BufferSize = 4;//缓存区大小,即传输计数器 0~65535
My_DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//传输方向
//DST是指外设站点作为目的地,SRC是指外设站点作为源头
My_DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//是否使用存储器到存储器,即选择软件触发还是硬件触发
My_DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;//存储器站点的起始地址
My_DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;//存储器站点的数据宽度
My_DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器站点地址是否递增
My_DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//传输模式,选择是否使用自动重装
//DMA的软件触发和自动重装不能同时启用,否则无法停下
My_DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;//外设站点的起始地址
My_DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外设站点的数据宽度
My_DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设外设站点地址是否递增。
My_DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//优先级配置。
DMA_Init(DMA1_Channel1,&My_DMA_InitStructure);
DMA_Cmd(DMA1_Channel1,ENABLE);//使能DMA
ADC_DMACmd(ADC1,ENABLE);//将ADC触发DMA开启
ADC_Cmd(ADC1,ENABLE);//开启ADC
//因使用多通道的ADC,要检验数据
//复位校准
ADC_ResetCalibration(ADC1);
//等待校准复位完毕
while(ADC_GetResetCalibrationStatus(ADC1) == SET);
//开启校准
ADC_StartCalibration(ADC1);
//等待校准完毕
while(ADC_GetCalibrationStatus(ADC1) == SET);
}
void Task02_AD_GetValue(void)
{
DMA_Cmd(DMA1_Channel1,DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel1,4);
DMA_Cmd(DMA1_Channel1,ENABLE);
ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发转换
//等待转运完成
while(DMA_GetFlagStatus(DMA1_FLAG_GL1) == RESET);
DMA_ClearFlag(DMA1_FLAG_GL1);
}