本帖最后由 鸡蛋鸭蛋荷包蛋 于 2023-2-1 13:40 编辑
#技术资源#
STM32 DMA定长发送+完成中断
代码测试完成,可以正常运行。 DMA发送需用到串口,故需配置串口。 串口配置串口配置很简单,照抄就行,这里用的正点原子的源码。 串口配置主要分为定义句柄、开启时钟、配置IO并写入相关寄存器、配置串口模式、开启中断(本代码没有用到接收,所以无所谓)、 GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
- 开启时钟(USART1是APB2时钟,IO口是PA9 PA10,A组IO口时钟也是APB2,故一起设置)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口1
USART_Cmd(USART1, ENABLE); //使能串口1
需使用DMA,故需在串口处使能DMA传输USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
DMA配置到此处,串口配置完毕,接下来配置DMA。 因为使用的是串口1,根据手册查询到对应的DMA为DMA1 4通道(TX)和DMA1 5通道(RX),只使用了TX串口,只需配置TX即可(即DMA1 4通道)。 根据结构体及成员相关解释,配置DMA1 4通道
先配置NVIC中断。
void DMA1_NVIC_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
//DMA1通道4 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
// //DMA1通道5 NVIC 配置
// NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn; //NVIC通道设置
// NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0 ; //抢占优先级
// NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级
// NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
}
配置DMA DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输
先调用函数,清除DMA1 4通道配置,配置完成后调用函数 DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE); //DMA通道4传输完成中断 配置DMA1 4通道传输完成中断,使能DMA1 4通道。 DMA_DeInit(DMA1_Channel4); //重置DMA1 4通道 USART1 TX
/********************************传输方向*****************************/
DMA_InitStructure.DMA_PeripheralBaseAddr=(u32)&USART1->DR;
DMA_InitStructure.DMA_MemoryBaseAddr=(u32) usart_txbuf; //自定义数组
DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST;
/********************************数据大小及模式*****************************/
DMA_InitStructure.DMA_BufferSize=USART1_MAX_TX_LEN; //数组大小
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
/********************************传输模式*****************************/
DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority=DMA_Priority_Medium;
DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;
DMA_Init(DMA1_Channel4,&DMA_InitStructure);
DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE); //DMA通道4传输完成中断
DMA_Cmd(DMA1_Channel4,ENABLE);
//DMA1通道4中断,判断上次数据串口是否已经发送完成
void DMA1_Channel4_IRQHandler(void)
{
if(DMA_GetITStatus(DMA1_IT_TC4)!= RESET) //DMA接收完成标志
{
LED1=!LED1;
DMA_ClearITPendingBit(DMA1_IT_TC4); //清除中断标志
}
}
到此,DMA1 4通道配置完成。
主函数
u16 t;
int main(void)
{
u16 i=0;
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
uart_init(115200); //串口初始化为9600
LED_Init(); //初始化与LED连接的硬件接口
DMA_init();
for(i=0;i<USART1_MAX_RX_LEN;i++)
{
usart_txbuf[i]=i;
}
while(1)
{
delay_ms(1000);
t++;
if(t%5==0){
dma1_channel4();
}
}
}
注意事项
- DMA发送完成一次后,需重新设置发送长度才可以再次使用。
/***********************设置DMA1 4缓存大小***********************/
void dma1_channel4(void)
{
DMA_Cmd(DMA1_Channel4,DISABLE); //关闭USART1 TX DMA1 所指示的通道
DMA_SetCurrDataCounter(DMA1_Channel4,USART1_MAX_RX_LEN);//DMA通道的DMA缓存的大小
DMA_Cmd(DMA1_Channel4, ENABLE); //使能USART1 TX DMA1 所指示的通道
}
- 使用DMA中断,需提前使能,才可以进入中断。我把串口DMA使能放在串口的初始化函数里,所以串口初始化需在DMA初始化函数之前。
注意代码执行顺序及代码逻辑,无法进入中断的问题找了好久。
|