/****************************************************************************** * 本文件实现串口发送功能(通过重构putchar函数,调用printf;或者USART_SendData() * 这里是一个用串口实现大量数据传输的例子,使用了DMA模块进行内存到USART的传输- W f5 f% _. X. e( J E8 p, C * 每当USART的发送缓冲区空时,USART模块产生一个DMA事件,7 ~, G! T: K m. U3 O * 此时DMA模块响应该事件,自动从预先定义好的发送缓冲区中拿出下一个字节送给USART * 整个过程无需用户程序干预,用户只需启动DMA传输传输即可" G6 a; S- b6 `/ [. a. u( h- S5 ` * 在仿真器调试时,可以在数据传输过程中暂停运行,此时DMA模块并没有停止 * 串口依然发送,表明DMA传输是一个独立的过程。 * 同时开启接收中断,在串口中断中将数据存入缓冲区,在main主循环中处理 * 作者:jjldc(九九) * 代码硬件基于万利199元的EK-STM32F开发板,CPU=STM32F103VBT64 t$ _! l) Q6 m8 G) K7 d& r *******************************************************************************/0 _: A& \9 @. _; x /* Includes ------------------------------------------------------------------*/ #include "stm32f10x_lib.h"* J7 ]* H9 x" e- Y% u #include "stdio.h"/ s3 A9 K* I' J. k( _& {, ?- G _2 Y) ^ /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ #define USART1_DR_Base 0x40013804. W% e8 ]; v$ J! r% T" C /* Private macro -------------------------------------------------------------*/" J1 c9 W# I8 A. S! l /* Private variables ---------------------------------------------------------*/& P5 `" N/ ^) u' j #define SENDBUFF_SIZE 10240 vu8 SendBuff[SENDBUFF_SIZE]; vu8 RecvBuff[10]; vu8 recv_ptr; /* Private function prototypes -----------------------------------------------*/ void RCC_Configuration(void); void GPIO_Configuration(void);, H, S) _, s% C6 d( v: ?4 q void NVIC_Configuration(void);6 H- A& z- E- ^/ P( i p6 L2 ? p void DMA_Configuration(void);9 X B. `* F% m5 `9 @$ F. e3 _, E void USART1_Configuration(void); int fputc(int ch, FILE *f);( e' t6 ]0 c0 s/ N6 ^# D void Delay(void);/ ~- e! m9 H4 B* V) X /* Private functions ---------------------------------------------------------*/ /*******************************************************************************; r7 p! Y% p/ c. M/ f, s1 {" } * Function Name : main * Description : Main program. * Input : None * Output : None- S( N3 m9 ?2 p9 t7 K, [( V * Return : None g% D1 x, h4 n! v& O *******************************************************************************/ int main(void) { u16 i; #ifdef DEBUG debug(); #endif recv_ptr = 0; RCC_Configuration(); GPIO_Configuration();3 C1 r" W- U5 r( k NVIC_Configuration(); DMA_Configuration(); USART1_Configuration(); printf("\r\nSystem Start...\r\n");4 Z! ^3 d1 d( _% X printf("Initialling SendBuff... \r\n"); for(i=0;i<SENDBUFF_SIZE;i++), M( p, G& ^6 N- }, q& G9 Y { SendBuff = i&0xff; } printf("Initial success!\r\nWaiting for transmission...\r\n");( ^* r. |" |4 N8 C //发送去数据已经准备好,按下按键即开始传输 while(GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_3)); x, w8 L3 p' c( M$ D1 }4 ? printf("Start DMA transmission!\r\n");! Z2 N1 ^) y; X }. h9 _$ @ //这里是开始DMA传输前的一些准备工作,将USART1模块设置成DMA方式工作7 W! O2 I# Z0 p. Q/ j USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); //开始一次DMA传输!# F. D3 S/ n- f7 g" j DMA_Cmd(DMA1_Channel4, ENABLE);4 h6 d" h: C4 z6 I/ K' I //等待DMA传输完成,此时我们来做另外一些事,点灯 //实际应用中,传输数据期间,可以执行另外的任务- j) f0 \0 h3 T7 a. i while(DMA_GetFlagStatus(DMA1_FLAG_TC4) == RESET) {: L& s0 p$ W0 q( K& M. Q) q LED_1_REV; //LED翻转 Delay(); //浪费时间 }$ a2 }2 K: {( L5 e* S! }0 B1 q //DMA传输结束后,自动关闭了DMA通道,而无需手动关闭 //下面的语句被注释& \& ^, l1 X1 M1 P //DMA_Cmd(DMA1_Channel4, DISABLE);7 y/ p. K; K; W- R# y1 z( |+ T8 h printf("\r\nDMA transmission successful!\r\n"); /* Infinite loop */ while (1)5 K: [+ A9 ^) ^! f; {6 J { } } /******************************************************************************* * Function Name : 重定义系统putchar函数int fputc(int ch, FILE *f) * Description : 串口发一个字节6 T, P: f. P$ q * Input : int ch, FILE *f5 p ~0 z- N: O9 U * Output :* C$ G2 H: y! P * Return : int ch * 这个是使用printf的关键: W0 w( ?( }5 m* f) }6 R W *******************************************************************************/ int fputc(int ch, FILE *f) { //USART_SendData(USART1, (u8) ch); USART1->DR = (u8) ch; /* Loop until the end of transmission */) V9 m u" `3 }1 K while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET) {7 p: j- C/ C4 ~; j } return ch; } /******************************************************************************* * Function Name : Delay' a2 h. {8 w) K( S4 M5 m * Description : 延时函数 * Input : None$ j h! g5 w4 s, M" ` * Output : None * Return : None3 W; [" ?% i, |$ V' D( }5 B A *******************************************************************************/* g6 y- T- ^2 R void Delay(void) { u32 i;9 v, p2 K% M2 G for(i=0;i<0xF0000;i++);4 v( O) x" } t1 X" B) ~4 [ return;# p5 O0 F; x# F$ H- f0 S+ R' w% U }! l% u" J4 }, F F /*******************************************************************************, t" Y$ l% ?' e$ o * Function Name : RCC_Configuration3 ]% o# H9 y% _. L * Description : 系统时钟设置; r0 O3 E* [$ ]! S2 l5 h * Input : None * Output : None * Return : None *******************************************************************************/ void RCC_Configuration(void)/ p5 Z( p, E) |: C0 E& \ { ErrorStatus HSEStartUpStatus;, m- c0 L9 E1 `6 h$ } //使能外部晶振 RCC_HSEConfig(RCC_HSE_ON);1 U' r' ^% a2 E //等待外部晶振稳定 HSEStartUpStatus = RCC_WaitForHSEStartUp();* h: K% A, y/ ^( L- b$ g //如果外部晶振启动成功,则进行下一步操作 if(HSEStartUpStatus==SUCCESS) {: x! |: x6 F& M% w //设置HCLK(AHB时钟)=SYSCLK RCC_HCLKConfig(RCC_SYSCLK_Div1);$ R3 B" ~! B% D2 \% O6 ^ //PCLK1(APB1) = HCLK/23 Z0 _; K9 Z/ f0 U! d RCC_PCLK1Config(RCC_HCLK_Div2);. M6 @7 X- {; e5 O/ N8 k$ t7 n7 S //PCLK2(APB2) = HCLK* |- B ?) R0 h) w RCC_PCLK2Config(RCC_HCLK_Div1);: w, z8 r1 v& u4 z# v //FLASH时序控制0 ?0 j5 x, i" u8 `% M. } //推荐值:SYSCLK = 0~24MHz Latency=0 // SYSCLK = 24~48MHz Latency=1 // SYSCLK = 48~72MHz Latency=2 FLASH_SetLatency(FLASH_Latency_2);9 f9 o# I2 M: j9 z- B' H" ?+ u //开启FLASH预取指功能, b( @) G1 x& U( j- q* [7 T FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //PLL设置 SYSCLK/1 * 9 = 8*1*9 = 72MHz RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //启动PLL1 @2 K( d4 E& {( }% l8 I3 j RCC_PLLCmd(ENABLE); //等待PLL稳定 while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //系统时钟SYSCLK来自PLL输出 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //切换时钟后等待系统时钟稳定$ i9 W% ~5 a. C while(RCC_GetSYSCLKSource()!=0x08);8 v5 D5 g/ `' y /*# x1 E) G% Q# r" v //设置系统SYSCLK时钟为HSE输入 RCC_SYSCLKConfig(RCC_SYSCLKSource_HSE); //等待时钟切换成功 while(RCC_GetSYSCLKSource() != 0x04); */ }) t3 E( b$ U/ T) }. v% d0 Y //下面是给各模块开启时钟 //启动GPIO RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | \9 i- x4 u) x' T/ B6 ` RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD,\ ENABLE); //启动AFIO RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //启动USART1 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //启动DMA时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);/ t' N6 F0 j3 }: `$ m } /******************************************************************************* * Function Name : GPIO_Configuration) w0 `6 B. T4 n1 V * Description : GPIO设置 * Input : None * Output : None * Return : None# l; z' E4 D3 b! P" g N* X *******************************************************************************/ 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; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;5 H% r- Z0 I" k8 Z GPIO_Init(GPIOC, &GPIO_InitStructure);5 x/ I8 a: @# A //KEY2 KEY3 JOYKEY //位于PD口的3 4 11-15脚,使能设置为输入$ T; r8 u! k' A: H" Y3 O4 H4 o 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;# O+ q8 }1 g8 L" u0 H2 J+ ~" x/ W1 U& { GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;8 Q7 G2 |/ I2 h0 P GPIO_Init(GPIOD, &GPIO_InitStructure); //USART1_TX GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;( I, v$ A b- A7 c GPIO_Init(GPIOA, &GPIO_InitStructure);7 J' e) N$ N! E //USART1_RX( v" _, u& Z2 i1 c GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;+ X* x; ^3 F# O! `2 ^. C1 N$ ` GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; v6 q9 O# l; ?8 [ GPIO_Init(GPIOA, &GPIO_InitStructure); } /******************************************************************************* * Function Name : NVIC_Configuration * Description : NVIC设置 * Input : None; J/ ~7 y1 P; O. O3 a; \ * Output : None * Return : None- }+ M# N; Q, N9 `! X: u9 U9 w! V *******************************************************************************/' y( K" A) s1 X7 {. H) o9 P' R void NVIC_Configuration(void). s) n. Y( \7 {- Y3 R { NVIC_InitTypeDef NVIC_InitStructure; #ifdef VECT_TAB_RAM // Set the Vector Table base location at 0x20000000& u" `9 G6 n6 J J NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); #else /* VECT_TAB_FLASH */ // Set the Vector Table base location at 0x08000000 NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);4 W+ K) |) Z9 L) f# N #endif //设置NVIC优先级分组为Group2:0-3抢占式优先级,0-3的响应式优先级 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); F9 z3 c" I: p$ L+ c* s3 j- T0 a //串口接收中断打开 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQChannel; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } /*******************************************************************************0 t) S6 j" g& C" M( F * Function Name : USART1_Configuration * Description : NUSART1设置 * Input : None) r' Y" j1 z) j' ~8 Q * Output : None * Return : None *******************************************************************************/ void USART1_Configuration(void) { USART_InitTypeDef USART_InitStructure;' {) a+ ?9 i L# [" [4 z USART_InitStructure.USART_BaudRate = 9600;' Q9 H6 g0 |0 D USART_InitStructure.USART_WordLength = USART_WordLength_8b;/ z; ~* r% c5 W ? 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 | USART_Mode_Rx;2 k8 ?+ Q2 [& x* D USART_Init(USART1, &USART_InitStructure); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);& |& P1 C2 ^* d2 D5 H' g USART_Cmd(USART1, ENABLE); }: b' a( V% ^, c1 _" h% l$ e( X) K void DMA_Configuration(void); Z- H8 A& F% `$ U( w3 w { DMA_InitTypeDef DMA_InitStructure; //DMA设置: //设置DMA源:内存地址&串口数据寄存器地址 //方向:内存-->外设 //每次传输位:8bit //传输大小DMA_BufferSize=SENDBUFF_SIZE* X) `) l0 ]2 k. l/ m //地址自增模式:外设地址不增,内存地址自增1 //DMA模式:一次传输,非循环 //优先级:中+ d& D o3 I/ h3 ~5 Y DMA_DeInit(DMA1_Channel4);" M9 O( B! x/ q! }4 q8 Y6 R* ~ DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base; DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;1 }, b2 r, j) T o DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;' T" |) G/ c! t' }+ s DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;5 P& x! h* g' R+ ]' i! N- W. Y DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;0 U3 b0 E8 ^; P7 d. Q DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; `4 D5 N' b& L DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;% ^, H& G" |) I; S DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;1 \( h% \- ?- a DMA_Init(DMA1_Channel4, &DMA_InitStructure);) c* c; t O# k7 Q7 s1 B }9 S$ @- `" U8 }" P |
STM32固件库分享,超全系列整理
STM32G030F6P6基于HAL库模拟SPI驱动1.8寸TFT LCD屏幕
STM32的CAN FD位定时设置注意事项
基于STM32将移植 SBSFU 到 STM32G070过程分享
基于STM32G030 RAM不够用经验分享
STM32G070在OLED上移植U8G2单色GUI
【经验分享】STM32 IAP+Ymodem功能实现(参考官方代码)
【经验分享】STM32的SPI问题
【经验分享】STM32 的加密实现
STM32G070—使用platformio+arduino