之前在用STM32进行开发的时候,有用到485进行数据的采集。因为数据量相对较大,所以开启了DMA通道。这边就对485串口配置和DMA的配置以及后续相关的收发操作进行一点总结。首先我485接的是板子上的串口一。根据手册可以看出这边使用的是DMA1的通道4和通道5,接下来就是相关的配置。PS:用库函数进行的开发
//一些定义
#define RX_LEN 1024
u8 real_recv_len ; //串口实际接收的数据长度
OS_SEM tx_sem; //发送的信号量
串口配置:
/*开启串口1 GPIO扣 DMA相关的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);//打开GPIOA的重映射功能
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/*配置RX TX的IO口功能*/
//PA10 RX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//PA9 TX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//UART1
USART_InitStructure.USART_BaudRate = 9600;//9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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_Tx; //先不使能接收 到相应task中使能
USART_Init(USART1, &USART_InitStructure);
USART_ClearFlag(USART1, USART_FLAG_TC);
USART_DMACmd(USART1, USART_DMAReq_Tx,ENABLE);
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);
//DMA1_CH4 USART1 TX
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR; //目标地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //方向
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_PeripheralDataSize_Byte; //内存字节为单位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //非内存到内存
DMA_Init(DMA1_Channel4, &DMA_InitStructure);
DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //方向
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
USART_Cmd(USART1, ENABLE);
接下来是中断函数的配置和进行数据的收发操作
首先定义DMA使能的宏定义
#define DMA1_CH4_EN() DMA1_Channel4->CCR |= DMA_CCR1_EN //开DMA
#define DMA1_CH5_EN() DMA1_Channel5->CCR |= DMA_CCR1_EN;//开DMA
#define DMA1_CH4_DIS() DMA1_Channel4->CCR &= (u16)(~DMA_CCR1_EN) //关DMA
#define DMA1_CH5_DIS() DMA1_Channel5->CCR &= (u16)(~DMA_CCR1_EN); //关DMA
接下来是中断函数的完成
void USART1_IRQHandler()
{
u32 temp;
int len;
u16 crc , crc_sum;
usart1_rx_dis(); //先关闭串口的接收
//清理中断标志位
temp = USART1->SR;
temp = USART1->DR;
real_recv_len = MODBUS_RX_LEN - DMA1_Channel5->CNDTR; //本次收到数据长度
//可以做你想要进行的操作,不过不要在中断函数中做太复杂的操作 也不要做一些数据量大的运行和浮点型的运行
...
...
...
//操作完使能串口1的接收
usart1_rx_en();
}
//下面是DMA的中断函数
void DMA1_Channel4_IRQHandler()
{
DMA1->IFCR = DMA1_IT_TC4; //清标志
DMA1_CH4_DIS(); //关发送DMA
isr_sem_send(&tx_sem); //设置信号量
}
//下面发送函数
static int usart1_send(u8* buf, u8 lenth)
{
//用信号量来判断DMA1是否发生中断
if(os_sem_wait(&tx_sem, 50) == OS_R_TMO)
{
DMA1_CH4_DIS();
}
DMA1_Channel4->CMAR = (u32)buf;
DMA1_Channel4->CNDTR = lenth;
usart1_rx_en();
DMA1_CH4_EN(); //发送
return 0;
}
//下面是通道一的接收初始化和使能失效函数的封装
//使能接收
static void usart1_rx_en()
{
DMA1_Channel5->CNDTR = RX_LEN;
DMA1_CH5_EN();
USART1->CR1 |= USART_Mode_Rx; //使能接收
}
//关闭接收
static void usart1_rx_dis()
{
USART1->CR1 &= ~USART_Mode_Rx; //关闭接收
DMA1_CH5_DIS(); //关接收DMA
}
//接收初始化
void lcd_rx1_init()
{
DMA1_Channel5->CNDTR = RX_LEN;
DMA1_Channel5->CMAR = (u32)modebus_rx_buff;
}
经过测试是可以正常使用的,其中大部分参考的是零死角玩转STM32这本书,书写的很好,很多例子很受用。