
/****************************************************************************** * 本文件实现串口发送功能(通过重构putchar函数,调用printf;或者USART_SendData() |2 x4 s4 Y+ K s+ [; V$ } * 这里是一个用串口实现大量数据传输的例子,使用了DMA模块进行内存到USART的传输 * 每当USART的发送缓冲区空时,USART模块产生一个DMA事件, * 此时DMA模块响应该事件,自动从预先定义好的发送缓冲区中拿出下一个字节送给USART * 整个过程无需用户程序干预,用户只需启动DMA传输传输即可 * 在仿真器调试时,可以在数据传输过程中暂停运行,此时DMA模块并没有停止7 c# f2 f" A1 x7 U& r * 串口依然发送,表明DMA传输是一个独立的过程。 * 同时开启接收中断,在串口中断中将数据存入缓冲区,在main主循环中处理 * 作者:jjldc(九九)4 c& _, @' O9 ?; G, l * 代码硬件基于万利199元的EK-STM32F开发板,CPU=STM32F103VBT6 *******************************************************************************/0 x5 v( U9 t9 J6 c /* Includes ------------------------------------------------------------------*/ #include "stm32f10x_lib.h"" p6 g5 q4 D, I; W, l #include "stdio.h" /* Private typedef -----------------------------------------------------------*/6 W) o% G( ?7 x) V6 K0 E) H /* Private define ------------------------------------------------------------*/5 h4 X( g% [) q( @ #define USART1_DR_Base 0x400138041 l; W9 F2 x w /* Private macro -------------------------------------------------------------*/0 f8 x- |+ ~; x /* Private variables ---------------------------------------------------------*/, r: F" \ D* _3 N, v #define SENDBUFF_SIZE 10240" z0 P3 n f2 u) a- O: y( P5 l. F% ] vu8 SendBuff[SENDBUFF_SIZE]; vu8 RecvBuff[10];4 v$ M. F( f+ Y vu8 recv_ptr; /* Private function prototypes -----------------------------------------------*/ void RCC_Configuration(void);+ ]3 N; }; p& L5 G- x) y2 ` void GPIO_Configuration(void); void NVIC_Configuration(void); void DMA_Configuration(void); void USART1_Configuration(void); int fputc(int ch, FILE *f); void Delay(void); /* Private functions ---------------------------------------------------------*/0 A- ?+ l- n' T8 g2 M /******************************************************************************* * Function Name : main f7 I0 q$ m: }8 f& c * Description : Main program. * Input : None * Output : None6 c. a5 P+ y. G. b6 N * Return : None *******************************************************************************/ int main(void) { u16 i; #ifdef DEBUG debug(); #endif8 P$ A: J* d1 `: N9 E& B: S6 P recv_ptr = 0; RCC_Configuration();5 B; q: L# A- R8 U GPIO_Configuration(); NVIC_Configuration(); DMA_Configuration();! n. Z9 S& q, y3 d( d. G USART1_Configuration(); printf("\r\nSystem Start...\r\n"); printf("Initialling SendBuff... \r\n"); for(i=0;i<SENDBUFF_SIZE;i++)! m/ o9 |( T$ }! Y& V. f {0 s1 @% Y# [/ D1 ^ SendBuff = i&0xff;+ e, L. q& W) B1 ` } printf("Initial success!\r\nWaiting for transmission...\r\n"); //发送去数据已经准备好,按下按键即开始传输/ m) `$ [. Y+ p q( } while(GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_3)); printf("Start DMA transmission!\r\n");4 w9 O" y" a- }0 o //这里是开始DMA传输前的一些准备工作,将USART1模块设置成DMA方式工作 USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); //开始一次DMA传输! DMA_Cmd(DMA1_Channel4, ENABLE); //等待DMA传输完成,此时我们来做另外一些事,点灯4 J9 j5 \# S8 C% j1 k2 I //实际应用中,传输数据期间,可以执行另外的任务 while(DMA_GetFlagStatus(DMA1_FLAG_TC4) == RESET)& N% p* V d) M% }4 B' y {5 o, L* M. \( ^9 h5 e. Q4 \/ m: ` LED_1_REV; //LED翻转. r0 v$ r( d( a N% ~# D Delay(); //浪费时间 }4 o" R( @+ P9 K6 v) o# O% e! s //DMA传输结束后,自动关闭了DMA通道,而无需手动关闭" v) G5 O) f2 W8 | x4 n. [- Q' l //下面的语句被注释) D, b. P2 H* l) K* ` //DMA_Cmd(DMA1_Channel4, DISABLE); printf("\r\nDMA transmission successful!\r\n");6 j) E3 m1 U0 X# f /* Infinite loop */ while (1) { } } /*******************************************************************************9 a# Z& t/ |7 q0 C5 {( B * Function Name : 重定义系统putchar函数int fputc(int ch, FILE *f)9 G( R: V. s: B5 G. Y$ k2 h) ^ * Description : 串口发一个字节9 W6 p* W: ^7 b- D& q2 v * Input : int ch, FILE *f" V. ~- e2 e2 I* B, G# Q7 ]' T' D0 l * Output : * Return : int ch4 U5 V, z0 q! t5 w * 这个是使用printf的关键 *******************************************************************************/, {) X! o4 q) P int fputc(int ch, FILE *f) {4 J. a. z. Y4 `# V4 T //USART_SendData(USART1, (u8) ch);+ f" g# S5 s/ ]' b+ s8 Z* V0 e USART1->DR = (u8) ch; /* Loop until the end of transmission */ while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET) {7 {) \$ W; B1 G% C( \ }. X2 Y% E* B. g- i* K return ch;& p) @ [3 W F7 W } /*******************************************************************************' M7 ?/ U; i, { * Function Name : Delay4 o- c+ F" u& Z2 p. B% ] * Description : 延时函数4 H6 B# J1 S# D* H& h) R% X * Input : None * Output : None * Return : None, m, A8 C' h% |! X' X+ I *******************************************************************************/$ n$ q0 ^5 S9 @ void Delay(void) {( ~* J1 t( n: T( ]) ?9 i6 I u32 i;5 r8 B, R/ V. Z4 b for(i=0;i<0xF0000;i++);6 V+ [7 ^9 W( @5 m6 x6 d3 p* j0 ? return; }% f( e6 B/ a7 @ /*******************************************************************************6 c* f/ n% m$ w1 e1 _; l" l& c * Function Name : RCC_Configuration * Description : 系统时钟设置; K$ b$ W. c" p0 g# C, X# Z8 \ * Input : None; I0 [/ l# E' ?# J+ L * Output : None * Return : None0 [7 |7 P# X+ w' G *******************************************************************************// u/ l \* G I3 H void RCC_Configuration(void) { ErrorStatus HSEStartUpStatus; //使能外部晶振9 I8 x$ H) I* N4 s0 |- n RCC_HSEConfig(RCC_HSE_ON);+ y/ Q" q, z6 V% f" @ //等待外部晶振稳定2 |- ?8 M8 t" p, W% A. i+ l$ d HSEStartUpStatus = RCC_WaitForHSEStartUp();: L% O- p- B+ i2 o& E7 Q //如果外部晶振启动成功,则进行下一步操作% A& p/ R" ^* P5 h- h A2 H, G if(HSEStartUpStatus==SUCCESS) { //设置HCLK(AHB时钟)=SYSCLK RCC_HCLKConfig(RCC_SYSCLK_Div1); //PCLK1(APB1) = HCLK/2 RCC_PCLK1Config(RCC_HCLK_Div2);5 I. W }' s1 ^" i0 i# u //PCLK2(APB2) = HCLK RCC_PCLK2Config(RCC_HCLK_Div1);5 C; a3 d. e5 l //FLASH时序控制# I& E1 S" p1 s- h0 N+ c/ G% E //推荐值:SYSCLK = 0~24MHz Latency=0 // SYSCLK = 24~48MHz Latency=1; N; H! f4 K( ~' H // SYSCLK = 48~72MHz Latency=23 j3 b" K! C7 w3 D! X FLASH_SetLatency(FLASH_Latency_2); //开启FLASH预取指功能 FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);/ O4 _" g. y7 v5 \' Q* O, \ //PLL设置 SYSCLK/1 * 9 = 8*1*9 = 72MHz4 o: X. e. B, y# k+ L RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //启动PLL8 `) m% U& a j% } RCC_PLLCmd(ENABLE);6 s' ~ l5 Q7 I2 G //等待PLL稳定 while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //系统时钟SYSCLK来自PLL输出 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //切换时钟后等待系统时钟稳定# D5 {, _& x5 q( g while(RCC_GetSYSCLKSource()!=0x08);. [( d5 c+ Z3 z2 z /*! M" Z0 w3 Q7 g //设置系统SYSCLK时钟为HSE输入; w5 p7 t B9 W RCC_SYSCLKConfig(RCC_SYSCLKSource_HSE);7 B( ]* t/ B, k8 R' T //等待时钟切换成功5 K6 V- i& k7 ` while(RCC_GetSYSCLKSource() != 0x04); */3 P1 s- H. S* O: R1 Z }6 v' F2 N6 P, l1 X+ l K //下面是给各模块开启时钟 //启动GPIO) s. B/ G- C+ M0 D8 x RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | \ RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD,\5 s7 Y0 s& w. e5 l5 M! ? y* z8 E ENABLE); //启动AFIO( j( {* z' u$ ]/ H3 O3 j$ j RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);7 |$ W. x$ [, E2 z' {0 m& n //启动USART19 w1 F; I2 h! l1 Y) [9 v: ? RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //启动DMA时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);. L* a5 P3 L8 y6 W" y. j6 ]! f }/ i: W' _6 }( \2 V# J( l' d /******************************************************************************* * Function Name : GPIO_Configuration * Description : GPIO设置6 P5 A* Q* S" v0 ^( n: a5 f * Input : None$ t' c+ e/ B' ~7 q/ K/ A. X3 B$ Y * Output : None * Return : None! m' r" B$ B/ N- ]) Q. t6 g! b *******************************************************************************/" h4 ^. |4 N. l$ C# A void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure;- F! c. I1 L3 S) H6 d& e1 n7 c //PC口4567脚设置GPIO输出,推挽 2M( \! Q$ `7 u6 `/ x% T GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;, @& v! j3 [3 N i6 J9 f, D GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;% b1 k2 u7 \8 e GPIO_Init(GPIOC, &GPIO_InitStructure); //KEY2 KEY3 JOYKEY4 R. k. F- \& d //位于PD口的3 4 11-15脚,使能设置为输入3 x6 U: b, e' ~ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_11 | GPIO_Pin_12 |\8 ^5 e+ J) `3 }* L8 O GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOD, &GPIO_InitStructure);1 |. f2 R& W7 O. t) c$ U //USART1_TX: u& U5 u' c4 q! _ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); //USART1_RX" l3 A% v9 N% o: x GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); }% o3 L( F k3 I% g /******************************************************************************* * Function Name : NVIC_Configuration( }0 T, P% Q0 T( ?: R0 | * Description : NVIC设置 * Input : None * Output : None8 V* M" S+ e6 n' l( F8 y: J1 T6 D3 M * Return : None *******************************************************************************/ void NVIC_Configuration(void)- f5 J) t: c4 M: H( z {2 f( z: [2 _( u( d8 C NVIC_InitTypeDef NVIC_InitStructure; #ifdef VECT_TAB_RAM& G8 Q! {- C7 g) I7 G // Set the Vector Table base location at 0x20000000 u9 ?/ \5 N/ \ [3 Y- l/ _ NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);6 [+ R# p. P+ { m1 [0 O4 o #else /* VECT_TAB_FLASH */ // Set the Vector Table base location at 0x08000000: G2 \& ~1 F/ _: t c' a NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);9 ]2 W4 T" t- n7 w3 ^5 |3 V4 q) m #endif+ a3 g, w6 `7 E% d1 C9 _9 @ //设置NVIC优先级分组为Group2:0-3抢占式优先级,0-3的响应式优先级* t# ?; C1 c8 W" C) w, N NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //串口接收中断打开 7 I* G. M, S' f7 w$ ` NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQChannel;+ `* [- n' d9 W1 O8 L1 ~ r NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;! V* W0 h. p I3 t( i# G2 S NVIC_Init(&NVIC_InitStructure);+ G8 v" o7 N- [+ y } /******************************************************************************* * Function Name : USART1_Configuration, n* x' z$ D, H2 G% ?; S+ j- ~ * Description : NUSART1设置9 ~& q( S" d. ^) ?5 a. E * Input : None8 B* ^9 l( j$ T6 K. ] * Output : None * Return : None *******************************************************************************/ void USART1_Configuration(void) { USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b;6 n) G$ i& R- \/ p8 q# y) H: o USART_InitStructure.USART_StopBits = USART_StopBits_1;9 U2 ~. i* h7 M! Z) E* b: V! c, q USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;% s) W6 ^" I0 I) z: B: y; F4 [2 i USART_Init(USART1, &USART_InitStructure);- M- W5 w" b; H% B USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);; O v/ x* \% }# Y( h USART_Cmd(USART1, ENABLE);+ a/ J L; l; M } void DMA_Configuration(void) ^, i! y# y: ?5 d3 K4 u {( P, [: Q' e: X3 D2 e DMA_InitTypeDef DMA_InitStructure; //DMA设置:- J1 w. `( ?% v //设置DMA源:内存地址&串口数据寄存器地址0 ?0 t) p% `" J' H //方向:内存-->外设 //每次传输位:8bit //传输大小DMA_BufferSize=SENDBUFF_SIZE //地址自增模式:外设地址不增,内存地址自增14 N+ } A3 j$ p' a: o* Z //DMA模式:一次传输,非循环 //优先级:中, l, C# [* F6 n) p2 E* c4 W DMA_DeInit(DMA1_Channel4);5 t2 N2 Y/ c; [8 G; f# A4 r' ` DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;* U V. y6 z; D! D. Z4 A4 j DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;6 X9 L( i$ q* |3 o O- | DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;: g) t- g1 W" x8 J% d DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;3 [; l" ?3 g) U7 n6 I/ T DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;8 o5 S6 J' R, F: ^0 ^ _4 ^ DMA_Init(DMA1_Channel4, &DMA_InitStructure); } |