STMCU小助手
发布时间:2022-1-18 19:42
|
/****************************************************************************** * 本文件实现串口发送功能(通过重构putchar函数,调用printf;或者USART_SendData()+ c( l8 B: m7 `! p z) } * 这里是一个用串口实现大量数据传输的例子,使用了DMA模块进行内存到USART的传输. J/ J" I& ~! l _ * 每当USART的发送缓冲区空时,USART模块产生一个DMA事件,: Y6 i# k6 a: [. e, n( Y * 此时DMA模块响应该事件,自动从预先定义好的发送缓冲区中拿出下一个字节送给USART) g4 b( L0 z7 q * 整个过程无需用户程序干预,用户只需启动DMA传输传输即可 * 在仿真器调试时,可以在数据传输过程中暂停运行,此时DMA模块并没有停止 * 串口依然发送,表明DMA传输是一个独立的过程。; N% P' k) }6 t * 同时开启接收中断,在串口中断中将数据存入缓冲区,在main主循环中处理 * 作者:jjldc(九九), ^. r3 E3 [" _. x( n * 代码硬件基于万利199元的EK-STM32F开发板,CPU=STM32F103VBT6 *******************************************************************************/" x% f/ c# U1 f1 o5 d; I( `& K. w0 r /* Includes ------------------------------------------------------------------*/6 O6 B p; {) N #include "stm32f10x_lib.h" #include "stdio.h"" R% ]( @) b/ T: j$ j# _6 o /* Private typedef -----------------------------------------------------------*/' t% r7 S- a8 x3 H% V3 Q /* Private define ------------------------------------------------------------*/ #define USART1_DR_Base 0x40013804 /* Private macro -------------------------------------------------------------*/! N. ~2 d" ]! s$ A0 H /* Private variables ---------------------------------------------------------*/ @2 h/ ^7 B5 |8 X, g3 x# f #define SENDBUFF_SIZE 10240* N+ f9 b- Z4 U' u vu8 SendBuff[SENDBUFF_SIZE];$ i0 l7 |, A: s6 e! E( S; M vu8 RecvBuff[10]; vu8 recv_ptr; /* Private function prototypes -----------------------------------------------*/. p/ s! Z9 W5 h U void RCC_Configuration(void);; } z, Y0 M2 z& _/ J& W void GPIO_Configuration(void);# f3 n6 D$ g) `% w( e$ A void NVIC_Configuration(void);4 h7 M' h' a- b& m. O9 c void DMA_Configuration(void); void USART1_Configuration(void);4 ~1 G9 _" S0 I- Y4 V! w4 I int fputc(int ch, FILE *f); void Delay(void); /* Private functions ---------------------------------------------------------*/, `; N) h" H& ~' G* g4 ^ /*******************************************************************************; t- @8 F O6 e: e3 F! U * Function Name : main; J9 i# E: Z) B4 W * Description : Main program. * Input : None; f8 M8 C4 @( e3 C& J! _% | * Output : None7 ^/ f& V0 `; ]6 K7 o* I * Return : None. K% L: q) `* C3 a' w *******************************************************************************/ int main(void)( Q) }6 V& {+ S. U4 }, m/ E { u16 i;; ~1 I: O/ g4 O) N' O #ifdef DEBUG% t7 c0 J4 N& e1 N debug();! D% e0 b( S0 H$ F #endif recv_ptr = 0; RCC_Configuration(); GPIO_Configuration();# u& J( s* t, K+ W4 c. K# d# e2 D, ` NVIC_Configuration(); DMA_Configuration(); USART1_Configuration();' `( Y: t. L( ]) }* s S& R) b printf("\r\nSystem Start...\r\n");% J& y6 |0 ]- w7 x1 ]5 L printf("Initialling SendBuff... \r\n"); for(i=0;i<SENDBUFF_SIZE;i++) {8 w Y9 ~2 _8 U8 w! Z SendBuff = i&0xff;$ N( Y; y: a8 n7 A. r: e }9 G ]* d y+ |: D! o% _ printf("Initial success!\r\nWaiting for transmission...\r\n"); //发送去数据已经准备好,按下按键即开始传输$ a8 Z4 m+ i+ x1 m1 m: ~ C while(GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_3));; R; b0 P9 d1 d, \2 U printf("Start DMA transmission!\r\n");9 c1 n4 d6 [- C+ j! { //这里是开始DMA传输前的一些准备工作,将USART1模块设置成DMA方式工作/ {* R% [% a9 {2 B, p USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); //开始一次DMA传输! DMA_Cmd(DMA1_Channel4, ENABLE); w( ~2 A) L: }& b! W& i9 x //等待DMA传输完成,此时我们来做另外一些事,点灯2 @5 C) p& l7 D( Z //实际应用中,传输数据期间,可以执行另外的任务 while(DMA_GetFlagStatus(DMA1_FLAG_TC4) == RESET) { LED_1_REV; //LED翻转 Delay(); //浪费时间 }- f8 ?( P) {2 W' f. m% @ //DMA传输结束后,自动关闭了DMA通道,而无需手动关闭! V" m, I/ d6 s' c' @2 x //下面的语句被注释 //DMA_Cmd(DMA1_Channel4, DISABLE);6 {( C" Q. e$ T% Z) t- } printf("\r\nDMA transmission successful!\r\n");. s( F9 ^" w# W$ c$ `# F* [( I /* Infinite loop */ while (1)* H$ B; L0 X# D. r8 h8 g {4 t1 ^. l' y' D7 p" U w9 z* t }8 n+ H$ `9 M8 G: G8 g, ` I* m# E } /*******************************************************************************& k5 M- e1 E( N0 z * Function Name : 重定义系统putchar函数int fputc(int ch, FILE *f)/ q; d& G, ], f" y1 z" |; x" m* u * Description : 串口发一个字节 * Input : int ch, FILE *f' _/ x* g' t4 W& [& n$ W6 u1 g * Output : * Return : int ch5 }2 L; [% \7 U* v: x8 V * 这个是使用printf的关键 *******************************************************************************/ int fputc(int ch, FILE *f) {4 [$ h$ g/ }$ k4 V% ^; Y( V2 H& M //USART_SendData(USART1, (u8) ch);& a! r7 ~& `* E2 `, X1 | USART1->DR = (u8) ch; /* Loop until the end of transmission */9 d2 }- Q# F& {& V) T while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)! K4 ]/ E: c2 ?3 r9 K { }3 N' {! s: g+ R& j. D$ S/ k return ch;, D; z, G4 [, j/ { o1 A, I" ?: _ }# N* A* |9 a# s. \1 j+ K, F /*******************************************************************************# B$ u# ]# C! o: r! s9 T * Function Name : Delay * Description : 延时函数/ ]1 o/ Y _4 \# d# v; y2 O& T * Input : None * Output : None d, b5 C+ n1 ]; ?$ c& ` * Return : None2 u6 ^ \# j+ c" A( l: }3 t! @9 ^ *******************************************************************************/2 Z" K; S' ?8 p- { void Delay(void) {$ |8 x9 z' }( q u32 i;9 O: Q$ k2 _0 q for(i=0;i<0xF0000;i++); return; }( C7 G/ d. J; Q /*******************************************************************************, w/ Y( p% H. S5 m* u * Function Name : RCC_Configuration: M- j0 b( i k2 D# ?* u9 B* c * Description : 系统时钟设置 * Input : None * Output : None * Return : None: r5 m% a8 {+ [1 k" _* F *******************************************************************************/2 d/ O; f3 `3 x5 m2 a0 c1 m void RCC_Configuration(void)3 w4 I1 A8 M$ V- s4 J { ErrorStatus HSEStartUpStatus; //使能外部晶振' G1 a# b: Z I5 b9 N- w/ Q- M5 N2 {( u RCC_HSEConfig(RCC_HSE_ON); //等待外部晶振稳定 HSEStartUpStatus = RCC_WaitForHSEStartUp();& L' t3 f1 {: s) D2 U //如果外部晶振启动成功,则进行下一步操作 if(HSEStartUpStatus==SUCCESS); h- T" R8 P W X+ ^8 E% m# h {# U" i( T& a; M* p, V //设置HCLK(AHB时钟)=SYSCLK RCC_HCLKConfig(RCC_SYSCLK_Div1);2 L6 Z) ^3 ]2 I8 ^ //PCLK1(APB1) = HCLK/29 X' K9 p& D& F1 h) Z* r RCC_PCLK1Config(RCC_HCLK_Div2);3 w6 p9 U4 J, K# ^1 _ //PCLK2(APB2) = HCLK7 W9 O# N* O. n RCC_PCLK2Config(RCC_HCLK_Div1); //FLASH时序控制 //推荐值:SYSCLK = 0~24MHz Latency=0 // SYSCLK = 24~48MHz Latency=1$ @' |0 [$ B6 R! a/ f/ Q // SYSCLK = 48~72MHz Latency=26 Q6 C) Z1 z. o/ p0 y2 h FLASH_SetLatency(FLASH_Latency_2);. p* i, _, ^, ^# I3 r# \& x2 w$ B //开启FLASH预取指功能 FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //PLL设置 SYSCLK/1 * 9 = 8*1*9 = 72MHz, Z& N( q1 {. A0 G. S" E: c3 o RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //启动PLL RCC_PLLCmd(ENABLE);, f9 x; P6 j5 X //等待PLL稳定/ H$ c8 k" T# N9 p8 Y7 h+ L while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //系统时钟SYSCLK来自PLL输出 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);% @8 h0 v# L. W: N //切换时钟后等待系统时钟稳定8 |+ Y# C+ S+ o5 G' a/ Z- B+ c/ | while(RCC_GetSYSCLKSource()!=0x08); /* //设置系统SYSCLK时钟为HSE输入$ v. k" P$ ?7 u0 } RCC_SYSCLKConfig(RCC_SYSCLKSource_HSE); //等待时钟切换成功4 {( d6 ?1 g4 E, c7 V while(RCC_GetSYSCLKSource() != 0x04);( L% n8 u7 I: f" W */ } k7 W9 R) n7 d1 r/ |: {7 G //下面是给各模块开启时钟 //启动GPIO RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | \ RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD,\, ?" X# J; y2 D& n$ c) O ENABLE);3 o6 k/ d+ t8 q- ^ //启动AFIO RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //启动USART1 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //启动DMA时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); }/ q% C0 `% u# v5 m /*******************************************************************************# ^: Q: L* a) P( p4 E5 B H * Function Name : GPIO_Configuration* |+ _; j# R) C- ]+ q7 G * Description : GPIO设置 * Input : None, U. }5 W/ h, j- ^& _, \ * Output : None * Return : None; {: _4 U& [5 P/ G6 _: U' u *******************************************************************************/ void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; //PC口4567脚设置GPIO输出,推挽 2M GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;8 \; J; e3 M8 ]/ Y" s GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;/ g# ?4 [2 m' p GPIO_Init(GPIOC, &GPIO_InitStructure); //KEY2 KEY3 JOYKEY/ \8 e1 d' X( `: ^2 y3 J2 r0 t3 P! L1 R //位于PD口的3 4 11-15脚,使能设置为输入) F, D6 [& C0 x# _% |5 g GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_11 | GPIO_Pin_12 |\" @( `& `. }9 g0 J; I) l GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOD, &GPIO_InitStructure);; `! v* r6 F) p0 W1 w' E //USART1_TX GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;9 k2 N Z/ z: P9 l1 I6 c8 I( P D: o0 S GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); //USART1_RX GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;% a0 c$ g: t& a2 ?0 N9 o GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure);1 r* Z0 {5 ?1 f) A) B: Y, J! T$ e9 { } /******************************************************************************* * Function Name : NVIC_Configuration$ I! J Y" j$ y * Description : NVIC设置 f$ H3 J. k" T+ H9 ` * Input : None * Output : None, V! N. W, F/ P( e3 K * Return : None+ S- P9 X1 U" c3 F$ Q *******************************************************************************/ void NVIC_Configuration(void) {2 R8 ]4 _4 U; u+ f7 M3 u( f NVIC_InitTypeDef NVIC_InitStructure; z3 a4 x, R) M! r; b/ E ]" W- y #ifdef VECT_TAB_RAM4 m2 B8 D8 F9 @ // Set the Vector Table base location at 0x20000000/ d2 z7 [8 h. w/ W4 _0 ~" @: j NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); #else /* VECT_TAB_FLASH */* G9 [ W7 G+ ~1 k9 z6 p6 w6 m // Set the Vector Table base location at 0x08000000! x6 y& ]3 l& S) m0 m4 T NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0); #endif! E% y; K$ {: \/ r7 C$ S //设置NVIC优先级分组为Group2:0-3抢占式优先级,0-3的响应式优先级 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);2 s( N+ J% i/ L) W! b //串口接收中断打开 3 F" W9 a6 l; i5 V; H$ @' O# R NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQChannel;' l: t- a3 |7 g7 H NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;4 p& E2 M3 [9 _; M NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;' e6 k4 z- k% ^: J( y+ g NVIC_Init(&NVIC_InitStructure);! }+ a5 M5 }! f0 e6 L- T }0 k/ L" @. |/ n( V+ n% L ~/ I. p /*******************************************************************************: K' X B! }/ T( Z * Function Name : USART1_Configuration * Description : NUSART1设置4 R6 R9 E3 u0 g: i * Input : None& T6 c6 c/ H9 g1 f) i: E& X * Output : None * Return : None *******************************************************************************/( [0 z4 V" w) [6 j2 _ void USART1_Configuration(void) {4 H) G# K+ M+ t& {" M USART_InitTypeDef USART_InitStructure;5 D: c* Q) ]9 O) _6 ^4 x2 M- H USART_InitStructure.USART_BaudRate = 9600; d- k( n" _$ f USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1;4 ]$ k' y4 a& @1 v, Y USART_InitStructure.USART_Parity = USART_Parity_No;9 Y1 h1 W: v8 A# t USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;6 g! m P A) A3 {! n, ? USART_Init(USART1, &USART_InitStructure);" d D5 x) v9 U$ N/ d% T USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); USART_Cmd(USART1, ENABLE); }4 p$ }7 Y" l8 c5 @ void DMA_Configuration(void)7 M% J( A1 _( \" N; z {) s/ n3 M+ ~) e8 S! O" J) j7 z DMA_InitTypeDef DMA_InitStructure;, `$ I. U% v8 b7 Y& d, B, z //DMA设置:) D7 [+ l; ^0 B' K9 a, E4 C //设置DMA源:内存地址&串口数据寄存器地址 //方向:内存-->外设 //每次传输位:8bit' `1 Y) i6 ]" v( j //传输大小DMA_BufferSize=SENDBUFF_SIZE //地址自增模式:外设地址不增,内存地址自增16 ]' U3 \1 C4 D" P //DMA模式:一次传输,非循环. i( y$ S6 f8 r" `! u, i) h //优先级:中 DMA_DeInit(DMA1_Channel4);1 l2 @5 ^( g: T2 m DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base; DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;/ h) j0 L, c& j/ x DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;& l% Q7 y/ C( p2 q+ [ DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;+ C% p0 ]' F1 G: v( W/ q% Q. s DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;, G* z2 R) i. I; D, ^& }3 I DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;( t1 |$ w+ x1 T4 f DMA_Init(DMA1_Channel4, &DMA_InitStructure); } |
实战经验 | 基于STM32G0B1的全桥移相+同步整流PWM解决方案
DMA 原理从 0 到 1:为什么它能解放 CPU?一文讲透 DMA 的工作流程
【福利三:逢7发帖赢大礼】8、基于STM32G070板子的Ymodem串口协议IAP Bootloader开发
【福利三:逢7发帖赢大礼】7、基于STM32G070板子TF卡的FATFS移植
【福利三:逢7发帖赢大礼】6、基于STM32G070板子spiflash的FATFS移植
【福利三:逢7发帖赢大礼】5、基于STM32G070板子IRDA红外遥控接收解码
【福利三:逢7发帖赢大礼】4、基于STM32G070板子SPI flash 移植SFUD库
【福利三:逢7发帖赢大礼】基于STM32G070板子的uart shell移植设计
【福利三:逢7发帖赢大礼】3、基于STM32G070板子的YModem串口协议通信
【福利三:逢7发帖赢大礼】2、基于STM32G070板子的OLED移植U8G2库
微信公众号
手机版