硬件:stm32f103cbt6
软件:STM32F10x_StdPeriph_Lib_V3.5.0
DMA,直接内存接受访问,类似用它随身释放的CPU的,灵魂,通过USART进行直接接收,接受使用DMA的方式,CPU不需要进行,当完成之后数据可以从内存的读取,从而减少了CPU的压力。
具体的代码实现如下:
- usart_driver.h 封装了接口,数据接收调用函数类型,基本数据结构等;
- usart_driver.c函数实现,中断服务函数实现等;
拷贝这两个文件即可,根据可以目录下的
参考用译文,进行初始化。
头文件
usart_driver.h已经声明了外部函数可能用到的接口;
USART3_DR地址的
因为USART3接收到数据会存在DR寄存器中,而DMA控制器则负责将该寄存器中的内容一一搬运到内存的缓冲区中(比如你定义的某个数组中),所以这里需要告诉DMA控制去哪里搬运,因此需要设置USART3_DR的总线地址。
USART3的如图所示;
DR基地址的移动地址图所示;
所以最终地址为:
0x40004800 + 0x004
#define USART_DR_Base 0x40004804
DMA的通道
有很多外设都可以使用DMA,例如ADC,I2C,SPI等等,所以,不同的外设选择属于自己的DMA通道,看参考手册;
因此USART3_RX会使用DMA1的
通道3,这都是自带的已经有了这个管道的好,我们需要遵循规则。
所以在代码中我们做出相应的定义;如下所示;
#define USART_Rx_DMA DMA1_Channel3
DMA的中断
DMA支持中断:传输过半,传输完成,传输出错;
因此在使用是相当安全也相当灵活,而这里只是用传输完成中断;如定义了,传输完成中断的标志位,DMA1_FLAG_TC3对应的TCIF;
#USART_Rx_DMA_FLAG DMA1_FLAG_TC3
USART接收接收函数
在STM32的HAL中封装了大量的外设,但是自己使用,但是非常方便,但是标准库中没有什么操作,这里我们接收接收就是实现,rx_cbk就是实现,即用户数据完成就可以开始注册了函数;
typedef void (*rx_cbk)(void* args);
通过使用usart_set_rx_cbk进行接口函数的注册,pargs为将传递的参数
路径;void usart_set_rx_cbk(uart_mod_t *pmod, rx_cbk pfunc,void *pargs);
头文件源码
#ifndef USART_DRIVER_H
#define USART_DRIVER_H
#include
#include
/* Private function prototypes -----------------------------------------------*/
#define USE_MICROLIB_USART 1
#if USE_MICROLIB_USART
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
//#define GETCHAR_PROTOTYPE int fgetc(FILE *f)
#endif /* __GNUC__ */
extern PUTCHAR_PROTOTYPE;
#else
#endif
//default 8N1
#define COM_PORT USART3
#define TX_PIN GPIO_Pin_10
#define RX_PIN GPIO_Pin_11
#define BAUDRATE 115200
#define IRQ_UART_PRE 3
#define IRQ_UART_SUB 3
#define USART_Rx_DMA_Channel DMA1_Channel3
#define USART_Rx_DMA_FLAG DMA1_FLAG_TC3
#define USART_DR_Base 0x40004804
#define USART_BUF_SIZE ((uint16_t)16)
typedef void (*rx_cbk)(void* args);
struct uart_mod {
uint8_t rx_buf[USART_BUF_SIZE];
uint8_t rx_dat_len;
uint8_t head;
uint8_t tail;
void (*init)(void);
void *pargs;
rx_cbk pfunc_rx_cbk;
};
typedef struct uart_mod uart_mod_t;
extern uart_mod_t user_uart_mod;
void usart_init(void);
void usart_set_rx_cbk(uart_mod_t *pmod, rx_cbk pfunc,void *pargs);
void usart_send_char(char ch);
void usart_test_echo(void);
uint8_t usart_recv_char(void);
int usart_printf(const char *fmt, ...);
//extern GETCHAR_PROTOTYPE;
#endif
DMA的基本配置
串口接收DMA的配置在函数dma_init中;
static void dma_init(void)
已经定义了数据缓冲区,如下:
uint8_t RxBuffer[USART_BUF_SIZE] = { 0 };
因此需要在 DMA 中设置 USART_DR 的地址,和数据大小配置的地址,以及接触;
还有就是数据流;
- 诉流向内存;
- 内存流向借款;
这个需要搞清楚;配置如下所示;
DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_Base;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)RxBuffer;
DMA_InitStructure.DMA_BufferSize = USART_BUF_SIZE;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
注意:
DMA_DIR_PeripheralSRC,外设作为源地址,数据是从外设地址向内存,即DMA数据从地址USART_DR_Base携带到RxBuffer去。
如果这个地方搞错了,会导致RxBuffer总是没有你想要的数据。
环形队列接收数据
线性缓冲区会因为缓冲器接收数据已满导致无法继续接收的问题;而环形队列进行接收的话,会自动进行覆盖,这样一来,在读取数据的时候,还要处理一个循环循环模式,下面的配置是把DMA配置为循环模式;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
在结构体_uart_mod中,则用两个变量变量单独的引导队首头和尾队;
具体的在函数中读取数据USART3_IRQHandler中,敏感数据从内存的RxBuffer读取到结构体user_uart_mod的成员rx_buf中;
最终调用回调函数。
硬件:stm32f103cbt6
软件:STM32F10x_StdPeriph_Lib_V3.5.0
DMA,直接内存接受访问,类似用它随身释放的CPU的,灵魂,通过USART进行直接接收,接受使用DMA的方式,CPU不需要进行,当完成之后数据可以从内存的读取,从而减少了CPU的压力。
具体的代码实现如下:
- usart_driver.h 封装了接口,数据接收调用函数类型,基本数据结构等;
- usart_driver.c函数实现,中断服务函数实现等;
拷贝这两个文件即可,根据可以目录下的
参考用译文,进行初始化。
头文件
usart_driver.h已经声明了外部函数可能用到的接口;
USART3_DR地址的
因为USART3接收到数据会存在DR寄存器中,而DMA控制器则负责将该寄存器中的内容一一搬运到内存的缓冲区中(比如你定义的某个数组中),所以这里需要告诉DMA控制去哪里搬运,因此需要设置USART3_DR的总线地址。
USART3的如图所示;
DR基地址的移动地址图所示;
所以最终地址为:
0x40004800 + 0x004
#define USART_DR_Base 0x40004804
DMA的通道
有很多外设都可以使用DMA,例如ADC,I2C,SPI等等,所以,不同的外设选择属于自己的DMA通道,看参考手册;
因此USART3_RX会使用DMA1的
通道3,这都是自带的已经有了这个管道的好,我们需要遵循规则。
所以在代码中我们做出相应的定义;如下所示;
#define USART_Rx_DMA DMA1_Channel3
DMA的中断
DMA支持中断:传输过半,传输完成,传输出错;
因此在使用是相当安全也相当灵活,而这里只是用传输完成中断;如定义了,传输完成中断的标志位,DMA1_FLAG_TC3对应的TCIF;
#USART_Rx_DMA_FLAG DMA1_FLAG_TC3
USART接收接收函数
在STM32的HAL中封装了大量的外设,但是自己使用,但是非常方便,但是标准库中没有什么操作,这里我们接收接收就是实现,rx_cbk就是实现,即用户数据完成就可以开始注册了函数;
typedef void (*rx_cbk)(void* args);
通过使用usart_set_rx_cbk进行接口函数的注册,pargs为将传递的参数
路径;void usart_set_rx_cbk(uart_mod_t *pmod, rx_cbk pfunc,void *pargs);
头文件源码
#ifndef USART_DRIVER_H
#define USART_DRIVER_H
#include
#include
/* Private function prototypes -----------------------------------------------*/
#define USE_MICROLIB_USART 1
#if USE_MICROLIB_USART
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
//#define GETCHAR_PROTOTYPE int fgetc(FILE *f)
#endif /* __GNUC__ */
extern PUTCHAR_PROTOTYPE;
#else
#endif
//default 8N1
#define COM_PORT USART3
#define TX_PIN GPIO_Pin_10
#define RX_PIN GPIO_Pin_11
#define BAUDRATE 115200
#define IRQ_UART_PRE 3
#define IRQ_UART_SUB 3
#define USART_Rx_DMA_Channel DMA1_Channel3
#define USART_Rx_DMA_FLAG DMA1_FLAG_TC3
#define USART_DR_Base 0x40004804
#define USART_BUF_SIZE ((uint16_t)16)
typedef void (*rx_cbk)(void* args);
struct uart_mod {
uint8_t rx_buf[USART_BUF_SIZE];
uint8_t rx_dat_len;
uint8_t head;
uint8_t tail;
void (*init)(void);
void *pargs;
rx_cbk pfunc_rx_cbk;
};
typedef struct uart_mod uart_mod_t;
extern uart_mod_t user_uart_mod;
void usart_init(void);
void usart_set_rx_cbk(uart_mod_t *pmod, rx_cbk pfunc,void *pargs);
void usart_send_char(char ch);
void usart_test_echo(void);
uint8_t usart_recv_char(void);
int usart_printf(const char *fmt, ...);
//extern GETCHAR_PROTOTYPE;
#endif
DMA的基本配置
串口接收DMA的配置在函数dma_init中;
static void dma_init(void)
已经定义了数据缓冲区,如下:
uint8_t RxBuffer[USART_BUF_SIZE] = { 0 };
因此需要在 DMA 中设置 USART_DR 的地址,和数据大小配置的地址,以及接触;
还有就是数据流;
- 诉流向内存;
- 内存流向借款;
这个需要搞清楚;配置如下所示;
DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_Base;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)RxBuffer;
DMA_InitStructure.DMA_BufferSize = USART_BUF_SIZE;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
注意:
DMA_DIR_PeripheralSRC,外设作为源地址,数据是从外设地址向内存,即DMA数据从地址USART_DR_Base携带到RxBuffer去。
如果这个地方搞错了,会导致RxBuffer总是没有你想要的数据。
环形队列接收数据
线性缓冲区会因为缓冲器接收数据已满导致无法继续接收的问题;而环形队列进行接收的话,会自动进行覆盖,这样一来,在读取数据的时候,还要处理一个循环循环模式,下面的配置是把DMA配置为循环模式;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
在结构体_uart_mod中,则用两个变量变量单独的引导队首头和尾队;
具体的在函数中读取数据USART3_IRQHandler中,敏感数据从内存的RxBuffer读取到结构体user_uart_mod的成员rx_buf中;
最终调用回调函数。
举报