实验:控制串口一以DMA方式发送(TX)数据
一、初始化DMA
对STM32任何模块使用前都要对其初始化、首先就是初始化外设时钟,查看时钟
数可知DMA时钟由AHB得来。
初始化时钟:RCC->AHBENR|=1<<0;
在读数据手册可知:直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。 两个DMA控制器有12个通道(DMA1有7个通道,DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。我们实验用的是串口一、查看外设与通道的对应关系如下:
所以我们初始化DMA1的第四通道。关于通道配置过程:
其中几个简单个人理解:
CPARx:就是串口发送数据的寄存器地址;
CMARx:就是DMA传输的数据的地址;
CMDTRx:就是传输的数据大小 ,按字节传输,传输后值递减;
void MYDMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar,u16 cndtr){
RCC->AHBENR|=1<<0; //开启DMA1时钟
delay_ms(5); //等待DMA时钟稳定
DMA_CHx->CPAR=cpar; //DMA1 外设地址
DMA_CHx->CMAR=(u32)cmar; //DMA1,存储器地址
DMA1_MEM_LEN=cndtr; //保存DMA传输数据量
DMA_CHx->CNDTR=cndtr; //DMA1,传输数据量
DMA_CHx->CCR=0X00000000; //复位
DMA_CHx->CCR|=1<<4; //从存储器读
DMA_CHx->CCR|=0<<5; //普通模式
DMA_CHx->CCR|=0<<6; //外设地址非增量模式
DMA_CHx->CCR|=1<<7; //存储器增量模式
DMA_CHx->CCR|=0<<8; //外设数据宽度为8位
DMA_CHx->CCR|=0<<10; //存储器数据宽度8位
DMA_CHx->CCR|=1<<12; //中等优先级
DMA_CHx->CCR|=0<<14; //非存储器到存储器模式
}
//开启一次DMA传输
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
{
DMA_CHx->CCR&=~(1<<0); //关闭DMA传输
DMA_CHx->CNDTR=DMA1_MEM_LEN; //DMA1,传输数据量
DMA_CHx->CCR|=1<<0; //开启DMA传输
}
初始化就基本完成。
在上面标识红色字体可知,开始DMA传输必须要有外设请求:
那么问题来了,请求信号是什么样的呢?
我们查看数据手册串口找到如下内容:
请求信号:就是配置USART_CR3,在实验中配置USART1->CR3=1<<7;
主函数如下:
#define SEND_BUF_SIZE 1200
u8 SendBuff[SEND_BUF_SIZE]; //发送数据缓冲区
const u8 TEXT_TO_SEND[]={"STM32F1 DMA 串口实验"};
int main(void)
{
u16 i;
u8 t=0;
u8 j,mask=0;
Stm32_Clock_Init(9); //系统时钟设置
uart_init(72,115200); //串口初始化为115200
delay_init(72); //延时初始化
KEY_Init(); //按键初始化
MYDMA_Config(DMA1_Channel4,(u32)&USART1->DR,(u32)SendBuff,SEND_BUF_SIZE);//DMA1通道4,外设为串口1,存储器为SendBuff,长度SEND_BUF_SIZE.
j=sizeof(TEXT_TO_SEND);
for(i=0;i
{
if(t>=j)//加入换行符
{
if(mask)
{
SendBuff[i]=0x0a;
t=0;
}else
{
SendBuff[i]=0x0d;
mask++;
}
}else//复制TEXT_TO_SEND语句
{
mask=0;
SendBuff[i]=TEXT_TO_SEND[t];
t++;
}
}
i=0;
while(1)
{
t=KEY_Scan(0);
if(t==KEY0_PRES)//KEY0按下
{
USART1->CR3=1<<7; //使能串口1的DMA发送
MYDMA_Enable(DMA1_Channel4);//开始一次DMA传输!
//等待DMA传输完成,此时我们来做另外一些事,点灯
//实际应用中,传输数据期间,可以执行另外的任务
while(1)
{
if(DMA1->ISR&(1<<13))//等待通道4传输完成
{
DMA1->IFCR|=1<<13;//清除通道4传输完成标志
break;
}
}
}
}
}
[cpp] view plain copy
如程序红色部分 传输过程出现错误或者传送完成我们可以设置标志位来提示:
这些标志位都在中断寄存器中:
我们用的是第四通道,所以是13位完成标志位,完成后再清掉中断可以接受下一次中断,清中断寄存器DMA_IFCR与ISR的位对应,这里就不解释了
关于DMA依据数据手册的简单实验到这里就结束了
关键字:STM32 DMA使用
引用地址:
STM32学习笔记之DMA使用
推荐阅读最新更新时间:2024-03-16 15:38
STM32 嵌入式学习入门(3)——STM32F103 按键输入控制LED灯
按键是单片机上一个很重要的输入设备,也很容易掌握,只要明白了IO口最基本的使用,就可以操作按键了。 我们的目的是控制开发板上板载的三个按键来操作开发板上板载的两个LED灯实现亮或灭(按键第一次按下时灯亮,再按下时灯灭,以此类推)。 博主所用的开发板是正点原子的mini板(STM32F103RCT6)和战舰板(STM32F103ZET6),因此下面的内容的例子以这两款开发板为例,但是基本的原理对任何开发板来说都是一样的,只要自己的开发板上板载了按键和LED灯(这两个资源应该是所有开发板上都有的资源吧),然后查看自己开发板的数据手册和硬件电路图、原理图,找到按键和LED灯对应的IO口,就可以按照本文所介绍的流程使用按键控制LE
[单片机]
STM32学习笔记之SysTick
#define SYSTICK_COUNTFLAG 16 //初始化延迟函数 void delay_init(void) { uint32_t ticks; ticks = SystemFrequency/1000; if (ticks SYSTICK_MAXCOUNT) while (1); SysTick- LOAD = (ticks & SYSTICK_MAXCOUNT) - 1; NVIC_SetPriority (SysTick_IRQn, (1 __NVIC_PRIO_BITS) - 1); SysTick- VA
[单片机]
STM32的RS485调试过程记录
RS485是半双工,RS422是全双工。 A接A,B接B,不要交叉。 RS485标准是4根线,定义如下: RO: Receiver Output: If A B by 200mV, RO will be high;If A B by 200mV, RO will be low.2 /RE: 接收器输出使能。当RE为低电平时,RO有效;当RE为高电平时,RO为高阻状态。 RI: Driver Input. A low on DI forces output Y low and output Z high. Similarly, a high on DI forces output Y high and output Z low
[单片机]
STM32的ADC用法
AD采样在电路中是一种比较常见的功能,可以用于电池电压检测、传感器值读取、信号采集等。STM32的ADC,由于引入了DMA,以及多种触发源,功能自然强大,用法也多种多样。今天,我们简单说说在单通道情况下,AD采样的几种用法。 1、AD单次转换+软件启动 最基本的用法,通过程序启动AD,AD采集一次,我们就去读一次。这种情况,建议开启AD转换完成中断,在中断中读出AD值并做处理。 这种方式的优点是配置简单,缺点么,太T么简单~ 初始化的时候,启动一次。然后在主循环里,每隔一秒启动一次。 在中断回调函数里,进行相关处理: 电脑输出如下: 2、连续转换+软件启动 在方法1的基础上做调整,从单次转换,变成连续转换。
[单片机]
stm32之TFT触摸屏(ILI9320)(2):由触摸屏写入EEPROM
程序来源是电阻触摸屏的触摸显示实验。TFT触摸屏呢,不仅仅是ILI9320这一个芯片,还涉及到两个,一个是ADS7846,一个是AT24CXX。这两个都分别是什么东西呢,ADS7846是TFT控制器,它的主要作用就是在按压之后,把这个值通过ADC进行模数转换,这个控制器是TFT屏幕必备的,当然可以有很多型号,这个ADS7846只是其中一种;这个AT24CXX则是EEPROM,XX不是真的字母X,而是省略了数字,比如AT24C02,这个是个2K的EEPROM,我们这里用的也是这款,它能干什么呢,存储数据,通过ADC得到的值需要变换为屏幕坐标什么的,这个EEPROM就可以存储这个基准值。 ADS7846的操作是通过SPI进行的
[单片机]
stm32外部中断实验
// 上一篇是关于串口通信的,用到GPIO的复用,将GPIO复用为usart串口; // 此处是利用按键进行中断处理,这里配置GPIO模式为输入,因为要接收按键的状态; //GPIO端口有很多,ABCD....但是中断只有22个,其中0~15个中断中断线与IO端口一一对应,需要配置GPIO与中断线的映射关系,(类似于端口复用)这里利用了函数:SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0); // 这里需要注意,使用的外部中断,先打开SYSCFG时钟,不然没法实现GPIO与中断线的映射。 #include stm32f4xx.h void init_led
[单片机]
关于STM32的计数与延时
Ⅰ关于STM32的计数和延时 在STM32中,具有计数(或计时)功能的模块基本都能实现延时功能。如:系统滴答SysTick、定时器TIM、实时时钟RTC、看门狗WDG。 精确延时一般使用定时器TIM即可实现。当然,是否精确,取决于你的主频(也就是晶振)是否准确,如果主频精确,那么实现的延时也一定精确。 一般来说,常温下实现us微秒级的延时,误差还是挺小的(应该说挺精确)。拿F407,主频168M来说,可以实现几十ns纳秒的延时,如果选用高精度的晶振,误差还是很小的。 总结:想要TIM定时器实现高精确的延时,就需要高精度的晶振。主频精确,那么延时就精确。 ⅡSTM32的TIM定时器 STM32的定时器有3类: 高级定
[单片机]
STM32在IAR中如何使用printf函数
STM32使用printf函数给串口打印信息的执行步骤为: 1.重定向printf函数 给uart.c文件中增加如下函数: int fputc(int ch, FILE *f) { USART_SendData(USART2, (unsigned char) ch);// USART1 可以换成 USART2 等 while (!(USART2- SR & USART_FLAG_TXE)); return (ch); } 2.增加头文件stdio #include stdio.h 3.添加宏 在IAR中使用printf应在Options- C/C++Compler- Preprocess
[单片机]