嵌入式stm32 复习(工作用)— USART(串口)通信原理知识 2020.3.23
添加链接描述
教你手写串口收发数据(看完这篇你就会手动写啦,保姆级讲解)---- 2020.3.28
添加链接描述
嵌入式stm32 复习(工作用)— DMA控制器知识 2020.3.30
添加链接描述
#define CR1_OVER8_Set ((u16)0x8000) /* USART OVER8 mode Enable Mask */
void USART_DMA_Init(void) {
//1.开启时钟
RCC->AHBENR |= 1 <CCR = 0;//先清零
DMA1_Channel5->CCR |= 1 <CCR |= 1 <CNDTR = USART1_REC_MAX - 1;//允许最大接收数据缓存大小,注意,必须在DMA停止时设置
//设置外设数据地址
DMA1_Channel5->CPAR = (u32) &USART1->DR;
//设置内存缓存器的地址
DMA1_Channel5->CMAR = (u32) USART1_REC;
//3.使能DMA
DMA1_Channel5->CCR |= 1 <CR1 & CR1_OVER8_Set) != 0) {
/* Integer part computing in case Oversampling mode is 8 Samples */
integerdivider = ((25 * apbclock) / (2 * bound));
} else /* if ((USARTx->CR1 & CR1_OVER8_Set) == 0) */
{
/* Integer part computing in case Oversampling mode is 16 Samples */
integerdivider = ((25 * apbclock) / (4 * bound));
}
tmpreg = (integerdivider / 100) <> 4));
/* Implement the fractional part in the register */
if ((USARTx->CR1 & CR1_OVER8_Set) != 0) {
tmpreg |= ((((fractionaldivider * 8) + 50) / 100)) & ((uint8_t) 0x07);
} else /* if ((USARTx->CR1 & CR1_OVER8_Set) == 0) */
{
tmpreg |= ((((fractionaldivider * 16) + 50) / 100)) & ((uint8_t) 0x0F);
}
return tmpreg;
}
void USART1_Init(int bound) {
//1.使能时钟
RCC->APB2ENR |= 1 <APB2ENR |= 1 <CRH &= ~(0x0F <CRH |= 0x0B < 50MHz,复用推免
GPIOA->CRH &= ~(0x0F <CRH |= 0x04 < 浮空输入模式
//3.初始化USART1
//3.1 设置波特率
USART1->BRR = USART_GetBound(USART1, bound);// 0x1D4 <CR1 &= ~(1 <CR1 &= ~(1 <CR2 &= ~(0x02 <CR1 |= 1 <CR1 |= 1 <CR1 |= 1 <CR3 |= 1 <CR1 |= 1 <CR1 |= 1 <SR & 0x10) { //总线空闲
USART1_STA = (USART1_REC_MAX - 1) - DMA1_Channel5->CNDTR;//实际接收的数据大小
USART1_REC[USART1_STA & 0x3FFF] = '\0';//保证有效字符串
USART1_STA |= 0x8000;//标识接收成功
USART1->DR;//清空状态
DMA1_Channel5->CCR &= ~(1 <CNDTR = USART1_REC_MAX - 1;//还原允许的数据缓冲区的大小
DMA1_Channel5->CCR |= (1 << 0);//启动DMA
}
}
void USART1_SendData(u8 *data, u8 len) {
u8 i = 0;
for (i = 0; i SR & 0x40) == 0)
;
USART1->DR = *(data + i); //等效于下面的函数
// USART_SendData(USART1, *(data + i));
}
}
int fputc(int ch, FILE* f) {
while ((USART1->SR & 0x40) == 0)
;
USART1->DR = (u8) ch; //等效于下面的函数
return ch;
}
好!按照老样子,接下来开始详细讲解每行代码的用处,以及为什么这样写!
串口DMA初始化部分
//1.开启时钟
RCC->AHBENR |= 1 << 0;//DMA1时钟
//
//由上图可知DMA是在AHB总线下的,所以需要开启AHB时钟,并且我们这里使用的是DMA1。
//
//
//2.配置DMA
DMA1_Channel5->CCR = 0;//先清零
DMA1_Channel5->CCR |= 1 <CCR |= 1 << 12;//中等优先级
//
//咦?这里为啥是反的?大家凑合看吧~
//
//因为这里使用过的就是USART1的读取,因为我们今天使用的过程就是通过RX读取外设上的数据,然后再通过DMA通道5来发送数据到存储器中。
//
//所以这里我们先清零,然后才能将我们需要写入的数据放到该寄存器中。其实另一方面也相当于设置成我们想要的配置了,大家往下看就知道了。
//
//因为我们这里是外设到存储器,所以该位为0。
////串口传输的长度是8位,所以11:10位也为0。
//
//这里我们外设地址不加一,所以该位为0。
//
//这里我们不执行循环操作,因为如果是循环操作的话,那么DMA写数据到存储器中,是不会管存储器缓存空间是否溢出,所以我们等等需要设置的就是得自己认为关闭掉DMA,然后再清空缓存区里面的数据,最后再开启,相当于是人为进行循环了。
////因为我们这里就是从外设读数据到存储器中,所以该位为0。
//由上图我们可以得知,存储器地址是加一的,外设地址是不加一的。
//
////这里我们设置为中等优先级,如果我们在用DMA传输的数据比较重要的话,在这里可以改成高或者最高优先级。
//设置DMA数据接收大小
DMA1_Channel5->CNDTR = USART1_REC_MAX - 1;//允许最大接收数据缓存大小,
//注意,必须在DMA停止时设置
//
//在前几篇关于串口收发数据的文章中我们可以得知USART1_REC_MAX是可以接收的最大数据长度,但是由于最后我们要表示该数据为有效数据,往往在最后加一个“/0”停止符。所以我们这里还是减一,留出一个空间来存放“/0”。
//设置外设数据地址
DMA1_Channel5->CPAR = (u32) &USART1->DR;
////这里就是将串口的数据赋值给这个寄存器,然后DMA再进行传输。
//设置内存缓存器的地址
DMA1_Channel5->CMAR = (u32) USART1_REC;
//
//最后存储器中的缓冲区为USART1_REC。
//3.使能DMA
DMA1_Channel5->CCR |= 1 << 0;
////这里就是开启DMA对应的通道,也相当于是使能。
串口DMA读取数据中断服务函数部分
if (USART1->SR & 0x10) { //总线空闲
//
//当检测到串口总线空闲,那么就代表数据传输完成。
//
USART1_STA = (USART1_REC_MAX - 1) - DMA1_Channel5->CNDTR;//实际接收的数据大小
//
//由上图我们可以得知,该寄存器代表的是现在的缓存区中还有多少个空余的缓存区。
那么我们想知道具体到底传输了多少个数据,那就可以用我们自己设置的最大可接受数据长度减掉这个寄存器值。
USART1_REC[USART1_STA & 0x3FFF] = '\0';//保证有效字符串
//由于上一篇文章中也讲了,所以这里就不再赘述了。
USART1_STA |= 0x8000;//标识接收成功
USART1->DR;//清空状态
//同理不再赘述。
DMA1_Channel5->CCR &= ~(1 <CNDTR = USART1_REC_MAX - 1;//还原允许的数据缓冲区的大小
DMA1_Channel5->CCR |= (1 << 0);//启动DMA
//在前面我们也说过我们设置的不是循环模式,所以如果想要源源不断的传输数据并且保证最终的存储区不溢出,那么我们就需要先关闭DMA,然后还原允许的数据缓冲区的大小,最后再启动DMA即可。
结束语个人认为大家如果细心看完这篇文章,并且结合上一篇文章一起看(在文章的刚开始会将前几篇关于USART和DMA原理部分的文章链接发出来),我相信大家会彻底掌握DMA了!!!如果觉得这篇文章还不错的话,记得点赞 ,支持下!!!
以后我会继续推出关于嵌入式(stm32)的协议方面的讲解,下一讲会推出PWM部分的文章!敬请期待!!!
**我先休息去了~~╭(╯^╰)╮