STMCU小助手
发布时间:2022-1-18 19:42
|
/******************************************************************************9 ~6 p* P. z$ h1 N/ a * 本文件实现串口发送功能(通过重构putchar函数,调用printf;或者USART_SendData()3 l8 {3 E2 A6 K) _* a* O * 这里是一个用串口实现大量数据传输的例子,使用了DMA模块进行内存到USART的传输1 i. Y) q8 X0 Q * 每当USART的发送缓冲区空时,USART模块产生一个DMA事件,* { J" o- }6 j9 q, n9 C * 此时DMA模块响应该事件,自动从预先定义好的发送缓冲区中拿出下一个字节送给USART! A3 W# Q. K# h s2 |8 [- q * 整个过程无需用户程序干预,用户只需启动DMA传输传输即可- ?, \1 M+ e% O * 在仿真器调试时,可以在数据传输过程中暂停运行,此时DMA模块并没有停止4 n2 [9 ^6 o' |& W9 t2 b * 串口依然发送,表明DMA传输是一个独立的过程。 * 同时开启接收中断,在串口中断中将数据存入缓冲区,在main主循环中处理 * 作者:jjldc(九九) * 代码硬件基于万利199元的EK-STM32F开发板,CPU=STM32F103VBT68 E6 x! j+ \) C- L8 T: [; o *******************************************************************************/% \0 h2 x9 b0 T- V! J# O$ Y1 T /* Includes ------------------------------------------------------------------*/" P$ y; p2 H- H/ R: P3 I$ Y6 ^ #include "stm32f10x_lib.h". D2 e, N7 w- u& J #include "stdio.h"- w$ r6 d' O6 X" ?4 _; t /* Private typedef -----------------------------------------------------------*/( _2 ^, U* [. r' A /* Private define ------------------------------------------------------------*/ #define USART1_DR_Base 0x40013804 /* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/' e# h" l) _% {1 s% m! s #define SENDBUFF_SIZE 10240# s: h) j0 w$ ?6 ] T vu8 SendBuff[SENDBUFF_SIZE];( ]7 h5 G5 u' Y& C vu8 RecvBuff[10]; vu8 recv_ptr;8 A& {3 v/ k' S( n- S /* Private function prototypes -----------------------------------------------*/4 ?. u, I# T/ \2 M6 @* G, x: A( c void RCC_Configuration(void); void GPIO_Configuration(void); void NVIC_Configuration(void);4 |+ e# [1 p ^/ q void DMA_Configuration(void); void USART1_Configuration(void); int fputc(int ch, FILE *f); void Delay(void); /* Private functions ---------------------------------------------------------*/5 Y. U9 T$ j% e3 @$ L$ L /******************************************************************************* * Function Name : main * Description : Main program. * Input : None * Output : None0 i# l# [2 F) f/ b( z( P * Return : None5 y& h% K K7 d' n8 s( q" p. U- T7 K *******************************************************************************/ int main(void) {% j) V" N" |2 n6 S u16 i;0 a. L# D+ O/ N: ]5 C- B) N #ifdef DEBUG) _& K* U& N; k& S4 f+ b# j debug();4 h9 g4 s) N( s& N2 J7 ]$ P3 {2 } #endif recv_ptr = 0; RCC_Configuration();( _+ q7 W. U& \: |7 d( L GPIO_Configuration(); NVIC_Configuration(); DMA_Configuration();0 x" u1 U) S Q6 v1 W5 ?3 C0 U N USART1_Configuration();) a- ]' y# m0 z( _+ H printf("\r\nSystem Start...\r\n"); printf("Initialling SendBuff... \r\n"); _- F! B; T! Q9 K7 o1 L P4 L for(i=0;i<SENDBUFF_SIZE;i++): T$ g; K( Q+ p+ i& w6 T6 r { SendBuff = i&0xff;6 ~0 e: g' Q+ E' O& t }; x% ~( r8 i" _0 p5 A1 }+ z+ K printf("Initial success!\r\nWaiting for transmission...\r\n");* h" }! q6 U# }7 e) x. p O& V8 n! b3 { //发送去数据已经准备好,按下按键即开始传输 while(GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_3)); printf("Start DMA transmission!\r\n"); //这里是开始DMA传输前的一些准备工作,将USART1模块设置成DMA方式工作" J/ I% ]* u- j- \0 F: x USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);8 F+ Z" d% c( C& q9 Z4 n' I/ E //开始一次DMA传输! DMA_Cmd(DMA1_Channel4, ENABLE); //等待DMA传输完成,此时我们来做另外一些事,点灯 //实际应用中,传输数据期间,可以执行另外的任务$ y2 H1 V3 j7 I% r" ^ while(DMA_GetFlagStatus(DMA1_FLAG_TC4) == RESET) { LED_1_REV; //LED翻转 Delay(); //浪费时间/ Y2 P8 }9 f' g1 R } //DMA传输结束后,自动关闭了DMA通道,而无需手动关闭* E' w: X1 `8 I" J# v9 w //下面的语句被注释 `; l$ {/ y7 l6 @" t# c/ e+ t1 I; T7 L# t //DMA_Cmd(DMA1_Channel4, DISABLE); printf("\r\nDMA transmission successful!\r\n");4 g$ d* T3 u3 S0 g7 e6 [ /* Infinite loop */+ T+ \* s/ [9 y7 ?5 ^& k- ~ while (1) { }, D1 `3 J/ N8 G% d( s7 N } /*******************************************************************************) ?* G: A$ K, z. Y9 X * Function Name : 重定义系统putchar函数int fputc(int ch, FILE *f)3 w; o* U6 n1 W- G * Description : 串口发一个字节: M( Z$ n& L$ u4 w* E! L * Input : int ch, FILE *f3 p& j1 N/ J& x2 j4 ^: w5 ] * Output :/ k+ P7 Y% ]" X1 r& m3 A3 r * Return : int ch * 这个是使用printf的关键! A3 k, \0 y6 m$ `6 ?. W *******************************************************************************/3 D7 U* j& t! |0 y% x5 j7 D" c int fputc(int ch, FILE *f) { //USART_SendData(USART1, (u8) ch);2 ?0 k x: b* K6 J USART1->DR = (u8) ch; /* Loop until the end of transmission */6 a s4 T' q( f, s# D' m, R while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET). a' e1 j6 G0 U { } return ch; } /******************************************************************************* * Function Name : Delay3 i$ k- ~, _- } * Description : 延时函数- R0 \/ B! B: w! J7 F( V * Input : None- x( O. O( R$ E * Output : None * Return : None1 V/ J$ b9 R( P; n *******************************************************************************/ void Delay(void)4 i' z# t! [5 A& X U4 v { u32 i; for(i=0;i<0xF0000;i++);1 X: f: S) e1 o+ \8 J) B0 ` return; }; z! L& m) _0 i A, d2 q /*******************************************************************************5 T- J9 \% T- ^( A1 s * Function Name : RCC_Configuration& p6 a. k- Q. I: M * Description : 系统时钟设置 * Input : None. R; y# i0 b/ L) I, i' L: I * Output : None, G& |3 a6 p- P * Return : None# J6 Z# Y! Z4 X% y! y& m *******************************************************************************/ void RCC_Configuration(void)- G+ L. R' z0 W {8 s( z& y9 e# R% m6 E) y ErrorStatus HSEStartUpStatus;1 F( e# U u# R& A* X' x //使能外部晶振/ M+ E: g* i2 ]0 ?6 m6 e RCC_HSEConfig(RCC_HSE_ON);/ f w' {3 y4 v$ Z //等待外部晶振稳定 HSEStartUpStatus = RCC_WaitForHSEStartUp(); //如果外部晶振启动成功,则进行下一步操作) N2 Z: T+ |( d/ g4 c; \: l* h if(HSEStartUpStatus==SUCCESS) { //设置HCLK(AHB时钟)=SYSCLK RCC_HCLKConfig(RCC_SYSCLK_Div1); //PCLK1(APB1) = HCLK/2- y. n( `6 R' i RCC_PCLK1Config(RCC_HCLK_Div2); //PCLK2(APB2) = HCLK RCC_PCLK2Config(RCC_HCLK_Div1); //FLASH时序控制& t7 i! {0 Y! w {8 p. H q4 M6 ^ //推荐值:SYSCLK = 0~24MHz Latency=06 V a% t/ w8 g- |( } // SYSCLK = 24~48MHz Latency=1 // SYSCLK = 48~72MHz Latency=2 FLASH_SetLatency(FLASH_Latency_2); //开启FLASH预取指功能; G' X# u1 x% m' M FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);* _9 K. p- \7 p1 Z' k //PLL设置 SYSCLK/1 * 9 = 8*1*9 = 72MHz RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //启动PLL RCC_PLLCmd(ENABLE);9 G7 ~( r6 c' ] G4 ^1 K* y //等待PLL稳定( _4 o# m4 h2 Q4 T3 A9 w; ~( |, Z while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //系统时钟SYSCLK来自PLL输出 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);! t( h% P) W$ H+ M //切换时钟后等待系统时钟稳定9 `3 R8 M9 ^3 k: w while(RCC_GetSYSCLKSource()!=0x08);" g7 d" i: q( Y! T( v3 t7 O, @ /* //设置系统SYSCLK时钟为HSE输入* ]+ A5 Q6 {0 J6 Q5 U RCC_SYSCLKConfig(RCC_SYSCLKSource_HSE); //等待时钟切换成功 while(RCC_GetSYSCLKSource() != 0x04);" h$ Q$ `3 m I* W& M */ } //下面是给各模块开启时钟 N9 V3 a8 i! M6 |6 c) @7 J //启动GPIO+ J. _5 s9 }0 i* }& Q RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | \ RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD,\! G, J0 @# Z% b# M! }' q ENABLE); //启动AFIO6 J5 N) ~% U8 \' ] h/ l RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //启动USART17 o# x* l$ V/ G# g3 {: d: T RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //启动DMA时钟4 ]/ f0 Z f% B' E! |0 A0 _ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);, }$ t2 v% H6 _$ ~4 O+ ` } /******************************************************************************* * Function Name : GPIO_Configuration * Description : GPIO设置4 Y. l( {- [7 ? * Input : None * Output : None6 K- Y) N0 D: s/ ?! t% a6 ` * Return : None *******************************************************************************/5 j* P5 n ?, |3 Y( T void GPIO_Configuration(void)1 j% ]( A$ C. p- { {5 M8 E9 V% f8 }. P! A GPIO_InitTypeDef GPIO_InitStructure; O0 I+ r @8 j+ F- Y Z* u- ?# T //PC口4567脚设置GPIO输出,推挽 2M GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;- Z3 U1 C0 V' J& c6 B1 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;7 ?) F9 `2 g9 R9 |# y. s GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init(GPIOC, &GPIO_InitStructure);7 s* A! i# t5 P4 c1 l* { //KEY2 KEY3 JOYKEY //位于PD口的3 4 11-15脚,使能设置为输入! @0 [+ w) ?& N8 o) c; R GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_11 | GPIO_Pin_12 |\ GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;3 Q5 T3 z# Q% h+ z1 [" n& W6 l* \ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;# H& w5 @6 H: y2 T! @5 i2 X1 K GPIO_Init(GPIOD, &GPIO_InitStructure);% c3 `! n( H( H1 d3 W3 e //USART1_TX GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;: i$ \6 x0 {) C, D' T9 B GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure);; w0 J' ]# T; Q1 x! [1 [' W: K% D/ K) ? //USART1_RX GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;# G* c& U! C1 c$ c: J GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;/ H7 z" F. ^, i# }9 I" O! { GPIO_Init(GPIOA, &GPIO_InitStructure); }. a5 f8 b0 p2 U7 F /******************************************************************************* * Function Name : NVIC_Configuration+ C* b$ c4 w# W * Description : NVIC设置* F3 g h) n0 _* W; G0 y; Z2 Q5 z/ w * Input : None * Output : None; v) J g& M2 } * Return : None *******************************************************************************/ void NVIC_Configuration(void)7 N: ~, R+ `$ O' |9 N2 g/ L6 D' I {6 }5 z" L6 b; H NVIC_InitTypeDef NVIC_InitStructure;9 P9 u2 R- g( a6 S/ \ S& C9 J #ifdef VECT_TAB_RAM // Set the Vector Table base location at 0x20000000 NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);- ~" j _0 l* L/ w& H. o# G9 E #else /* VECT_TAB_FLASH */ // Set the Vector Table base location at 0x08000000) d* s/ F; _* H u6 u" O2 U NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0); #endif //设置NVIC优先级分组为Group2:0-3抢占式优先级,0-3的响应式优先级 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //串口接收中断打开 9 [' C9 n, ] M3 ]) }8 W NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQChannel; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;) \+ L* @1 T0 K. i9 [) G NVIC_Init(&NVIC_InitStructure);. W+ v0 e; w. Q* g }+ N% n7 m4 w2 J- ~) c, Z /******************************************************************************* * Function Name : USART1_Configuration * Description : NUSART1设置 * Input : None/ E* Q; R8 V. g$ d! v/ k( t * Output : None2 B/ C9 A" T" g8 |" B * Return : None& |) n; @8 r6 \, M *******************************************************************************/ void USART1_Configuration(void) {6 [. z6 D% J+ b1 T USART_InitTypeDef USART_InitStructure;3 X, ~% L+ }- E1 x! p USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b;! V' `7 {4 W. w) r0 v USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No;; s4 m: @4 m L1 e2 j# r USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;& B$ R% W' O; u, y& k0 N- o* w USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;2 x- S4 g5 Z5 T7 r# M: \7 h" F USART_Init(USART1, &USART_InitStructure);+ P8 f6 z! C$ V' g$ X4 l USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); USART_Cmd(USART1, ENABLE);- J- o- s7 m$ U0 b }0 e5 W; U- S3 t( `& y$ s void DMA_Configuration(void) {8 x0 P* S! [* k: n g% H3 d DMA_InitTypeDef DMA_InitStructure; //DMA设置:4 d/ E/ ^- ^$ a+ k0 { //设置DMA源:内存地址&串口数据寄存器地址 //方向:内存-->外设 x" j2 o: E' b' t* m/ ^7 f2 I //每次传输位:8bit //传输大小DMA_BufferSize=SENDBUFF_SIZE/ ~# F" u! P1 F+ O. q) c" v //地址自增模式:外设地址不增,内存地址自增19 p0 T2 [, @3 s0 b6 `2 ^9 w //DMA模式:一次传输,非循环4 k( _; H& O- `- z //优先级:中; f! a( t; H g3 Q DMA_DeInit(DMA1_Channel4);3 R/ f" i$ p2 E/ q! F DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base; DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;- q; ~9 W5 H; }& ]. m8 [* ]3 H+ W DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; 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;; u) q7 p. e- P( } DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;2 N; {% @% i3 Y% i 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); } |
实战经验 | 基于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库
微信公众号
手机版