搜档网
当前位置:搜档网 › 零死角玩转stm32-中级篇1、调试必备-串口(UART)

零死角玩转stm32-中级篇1、调试必备-串口(UART)

零死角玩转stm32-中级篇1、调试必备-串口(UART)
零死角玩转stm32-中级篇1、调试必备-串口(UART)

0、友情提示

《零死角玩转STM32》系列教程由初级篇、中级篇、高级篇、系统篇、四个部分组成,根据野火STM32开发板旧版教程升级而来,且经过重新深入编写,重新排版,更适合初学者,步步为营,从入门到精通,从裸奔到系统,让您零死角玩转STM32。M3的世界,与野火同行,乐意惬无边。

另外,野火团队历时一年精心打造的《STM32库开发实战指南》将于今年10月份由机械工业出版社出版,该书的排版更适于纸质书本阅读以及更有利于查阅资料。内容上会给你带来更多的惊喜。是一本学习STM32必备的工具书。敬请期待!

1、调试必备-串口(USART1)

当我们在学习一款CPU的时候,最经典的实验莫过于流水灯了,会了流水灯的话就基本等于学会会操作I/O口了。那么在学会操作I/O之后,面对那么多的片上外设我们又应该先学什么呢?有些朋友会说用到什么就学什么,听起来这也不无道理呀。

但对于野火来说会把学习串口的操作放在第二位。在程序运行的时候我们可以通过点亮一个LED来显示代码的执行的状态,但有时候我们还想把某些中间量或者其他程序状态信息打印出来显示在电脑上,那么这时串口的作用就可想而知了。

1.1 异步串口通讯协议

阅读过《STM32中文参考手册》的读者会发现,STM32的串口非常强大,它不仅支持最基本的通用串口同步、异步通讯,还具有LIN总线功能(局域互联网)、IRDA功能(红外通讯)、SmartCard功能。

为实现最迫切的需求,利用串口来帮助我们调试程序,本章介绍的为串口最基本、最常用的方法,全双工、异步通讯方式。图 1-1为串口异步通讯协议。

图 1-1 异步串口通讯协议

重温串口的通讯协议,我们知道要配置串口通讯,至少要设置以下几个参数:字长(一次传送的数据长度)、波特率(每秒传输的数据位数)、奇偶校验位、还有停止位。对ST库函数的使用已经上手的读者应该能猜到,在初始化串口的时候,必然有一个串口初始化结构体,这个结构体的几个成员肯定就是有来存储这些控制参数的。

1.2 直通线和交叉线

野火STM32开发板串口硬件原理图

图 1-2 野火开发板串口硬件图

见图 1-2,这是野火STM32开发板的接线图,使用的为MAX3232芯片,把STM32的PA10引脚(复用功能为USART1的Rx)接到了DB9接口的第2针脚,把PA9引脚(复用功能为USART的Tx)连接到了DB9接口的第3针脚。

Tx(发送端)接第3针脚,Rx(接收端)接第2针脚。这种接法是跟PC 的串口接法一样的,如果要实现PC跟野火板子通讯,就要使用两头都是母的交叉线。

串口线主要分两种,直通线(平行线)和交叉线。它们的区别见图 1-3。假如PC与板子之间要实现全双工串口通讯,必然是PC的Tx针脚要连接到板子的Rx针脚,而PC的Rx针脚则要连接至板子的Tx针脚了。由于板子和pc 的串口接法是相同的,就要使用交叉线来连接了。如果有的开发板是Tx连接至DB9的第2针脚,而Rx连接至第3针脚,这与PC接法是相反的,这样的板子与PC通讯就需要使用直通线了。

为什么野火板子要使用PC的接法?

假如使用非PC接法,由于板子与PC的接法相反,通讯就要使用直通线;但两个板子之间想要进行串口通讯时,由于接法相同,就要使用交叉线。如果使用PC接法,板子与PC之间接法相同,通讯使用交叉线;两个相同板子之间接法也相同,通讯也是使用交叉线。

所以野火建议大家设计板子时,尽量采用与PC相同的标准串口接法。

图 1-3 交叉线与直通线的区别

介绍直通线与交叉线的区别,一来是野火发现某些读者因为线的问题而花费了大量宝贵的时间。二来是介绍串口线的DIY方法。要实现基本的全双工异步通讯,只要3条线,分别为Rx、Tx、和GND。如果读者正在为直通线、交叉线、公头、母头不匹配而烦恼,可以根据自己的需要,参照图 1-3,用三根杜邦线连接即可。

1.3 串口工作过程分析

图 1-4 串口架构图

串口外设的架构图看起来十分复杂,实际上对于软件开发人员来说,我们只需要大概了解串口发送的过程即可。

从下至上,我们看到串口外设主要由三个部分组成,分别是波特率的控制部分、收发控制部分及数据存储转移部分。

1.3.1波特率控制

波特率,即每秒传输的二进制位数,用 b/s (bps)表示,通过对时钟的控制可以改变波特率。在配置波特率时,我们向波特比率寄存器USART_BRR写入参数,修改了串口时钟的分频值USARTDIV。USART_BRR寄存器包括两部分,分别是DIV_Mantissa(USARTDIV的整数部分)和DIVFraction(USARTDIV 的小数)部分,最终,计算公式为

USARTDIV=DIV_Mantissa+(DIVFraction/16)。

USARTDIV是对串口外设的时钟源进行分频的,对于USART1,由于它是挂载在APB2总线上的,所以它的时钟源为f PCLK2;而USART2、3挂载在APB1上,时钟源则为f PCLK1,串口的时钟源经过USARTDIV分频后分别输出作为发送器时钟及接收器时钟,控制发送和接收的时序。

1.3.2收发控制

围绕着发送器和接收器控制部分,有好多个寄存器:CR1、CR2、CR3、SR,即USART的三个控制寄存器(Control Register)及一个状态寄存器(Status Register)。通过向寄存器写入各种控制参数,来控制发送和接收,如奇偶校验位,停止位等,还包括对USART中断的控制;串口的状态在任何时候都可以从状态寄存器中查询得到。具体的控制和状态检查,我们都是使用库函数来实现的,在此就不具体分析这些寄存器位了。

1.3.3数据存储转移部分

收发控制器根据我们的寄存器配置,对数据存储转移部分的移位寄存器进行控制。

当我们需要发送数据时,内核或DMA外设(一种数据传输方式,在下一章介绍)把数据从内存(变量)写入到发送数据寄存器TDR后,发送控制器将适时地自动把数据从TDR加载到发送移位寄存器,然后通过串口线Tx,把数据一位一位地发送出去,在数据从TDR转移到移位寄存器时,会产生发送寄存器

TDR已空事件TXE,当数据从移位寄存器全部发送出去时,会产生数据发送完成事件TC,这些事件可以在状态寄存器中查询到。

而接收数据则是一个逆过程,数据从串口线Rx一位一位地输入到接收移位寄存器,然后自动地转移到接收数据寄存器RDR,最后用内核指令或DMA 读取到内存(变量)中。

1.4 串口通讯实验分析

1.4.1实验描述及工程文件清单

实验描述重新实现C库中的printf() 函数到串口1,这样我们就可以

像用C库中的printf() 函数一样将信息通过串口打印到电

脑,非常方便我们程序的调试。

硬件连接PA9 - USART1(Tx)

PA10 - USART1(Rx)

用到的库文件startup/start_stm32f10x_hd.c

CMSIS/core_cm3.c

CMSIS/system_stm32f10x.c

FWlib/stm32f10x_gpio.c

FWlib/stm32f10x_rcc.c

FWlib/stm32f10x_usart.c

用户编写的文件USER/main.c

USER/stm32f10x_it.c

USER/usart1.c

1.4.2 配置工程环境

串口实验中我们用到了GPIO、RCC、USART这三个外设的库文件

stm32f10x_gpio.c、stm32f10x_rcc.c、stm32f10x_usart.c,所以我们先要把这

个库文件添加进工程,新建用户文件usart1.c 。并在stm32f10x_conf.h 中把相应的头文件的注释去掉。

1./**

2. ****************************************************************

3. * @file Project/STM32F10x_StdPeriph_Template/stm32f10x_conf.h

4. * @author MCD Application Team

5. * @version V3.5.0

6. * @date 08-April-2011

7. * @brief Library configuration file.

8. ******************************************************** /

9.

10.#include "stm32f10x_gpio.h"

11.#include "stm32f10x_rcc.h"

12.#include "stm32f10x_usart.h"

1.4.3 main文件

配置好要用的库的环境之后,我们就从main函数看起,层层剥离源代码。

1./*

2. * 函数名:main

3. * 描述:主函数

4. * 输入:无

5. * 输出:无

6. */

7.int main(void)

8.{

9./* USART1 config 115200 8-N-1 */

10. USART1_Config();

11.

12. printf("\r\n this is a printf demo \r\n");

13.

14. printf("\r\n 欢迎使用野火M3实验板:) \r\n");

15.

16. USART1_printf(USART1, "\r\n This is a USART1_printf demo \r\n");

17.

18. USART1_printf(USART1, "\r\n ("__DATE__ " -

" __TIME__ ") \r\n");

19.

20.for(;;)

21. {

22.

23. }

24.}

首先调用函数USART1_Config(),函数USART1_Config()主要做了如下工作:

1.使能了串口1的时钟

2.配置好了usart1的I/O

3.配置好了usart1的工作模式,具体为波特率为 115200 、8个数据位、1

个停止位、无硬件流控制。即 115200 8-N-1。

1.4.4 USART初始化配置

具体的USART1_Config()在usart1.c这个用户文件中实现:

1./*

2. * 函数名:USART1_Config

3. * 描述:USART1 GPIO 配置,工作模式配置。115200 8-N-1

4. * 输入:无

5. * 输出 : 无

6. * 调用:外部调用

7. */

8.void USART1_Config(void)

9.{

10. GPIO_InitTypeDef GPIO_InitStructure;

11. USART_InitTypeDef USART_InitStructure;

12.

13./* config USART1 clock */

14. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIO

A, ENABLE);

15.

16./* USART1 GPIO config */

17./* Configure USART1 Tx (PA.09) as alternate function push-

pull */

18. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;

19. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

20. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

21. GPIO_Init(GPIOA, &GPIO_InitStructure);

22./* Configure USART1 Rx (PA.10) as input floating */

23. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;

24. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

25. GPIO_Init(GPIOA, &GPIO_InitStructure);

26.

27./* USART1 mode config */

28. USART_https://www.sodocs.net/doc/402452638.html,ART_BaudRate = 115200;

29. USART_https://www.sodocs.net/doc/402452638.html,ART_WordLength = USART_WordLength_8b;

30. USART_https://www.sodocs.net/doc/402452638.html,ART_StopBits = USART_StopBits_1;

31. USART_https://www.sodocs.net/doc/402452638.html,ART_Parity = USART_Parity_No ;

32. USART_https://www.sodocs.net/doc/402452638.html,ART_HardwareFlowControl = USART_HardwareFlow

Control_None;

33. USART_https://www.sodocs.net/doc/402452638.html,ART_Mode = USART_Mode_Rx | USART_Mode_Tx;

34. USART_Init(USART1, &USART_InitStructure);

35. USART_Cmd(USART1, ENABLE);

36.}

在代码的第14行,调用了库函数RCC_APB2PeriphClockCmd()初始化了USART1和GPIOA的时钟,这是因为使用了GPIOA的PA9和PA10的默认复用USART1的功能,在使用复用功能的时候,要开启相应的功能时钟USART1。

接下来,这段串口初始化代码分为两个部分,第一部分为GPIO的初始化,第二部分才是串口的模式、波特率的初始化。

1.4.4.1 GPIO初始化

在错误!未找到引用源。提到过,GPIO具有默认的复用功能,在使用它的复用功能的时候,我们首先要把相应的GPIO进行初始化。此时我们使用的GPIO的复用功能为串口,但为什么是PA9和PA10用作串口的Tx和Rx,而不是其它GPIO引脚呢?这是从《STM32F103CDE增强型系列数据手册》的引脚功能定义中查询到的。

图 1-5 GPIO引脚功能说明图

选定了这两个引脚,并且PA9为Tx,PA10为Rx,那么它们的GPIO模式要如何配置呢?Tx为发送端,输出引脚,而且现在GPIO是使用复用功能,所以要把它配置为复用推挽输出(GPIO_Mode_AF_PP) ;而Rx引脚为接收端,输入引脚,所以配置为浮空输入模式GPIO_Mode_IN_FLOATING。如果在使用复用功能的时候,对GPIO的模式不太确定的话,我们可以从《STM32参考手册》的GPIO章节中查询得到,见图 1-6。

图 1-6 GPIO复用功能模式设置

1.4.4.2 USART初始化

从代码的第28行开始,进行USART1的初始化,也就是填充USART的初始化结构体。这部分内容,是根据串口通讯协议来设置的。

https://www.sodocs.net/doc/402452638.html,ART_BaudRate = 115200;

波特率设置,利用库函数,我们可以直接这样配置波特率,而不需要自行计算USARTDIV的分频因子。在这里把串口的波特率设置为115200,也可以设置为9600等常用的波特率,在《STM32参考手册》中列举了一些常用的波特率设置及其误差,见图 1-7。如果配置成9600,那么在和PC通讯的时候,也应把PC的串口传输波特率设置为相同的9600。通讯协议要求两个通讯器件之间的波特率、字长、停止位奇偶校验位都相同。

图 1-7 STM32常用波特率及其误差

https://www.sodocs.net/doc/402452638.html,ART_WordLength = USART_WordLength_8b;

配置串口传输的字长。本例程把它设置为最常用的8位字长,也可以设置为9位。

https://www.sodocs.net/doc/402452638.html,ART_StopBits = USART_StopBits_1;

配置停止位。把通讯协议中的停止位设置为1位。

https://www.sodocs.net/doc/402452638.html,ART_Parity = USART_Parity_No ;

配置奇偶校验位。本例程不设置奇偶校验位。

https://www.sodocs.net/doc/402452638.html,ART_HardwareFlowControl= USART_HardwareFlowControl_None;

配置硬件流控制。不采用硬件流。

硬件流,在STM32的很多外设都具有硬件流的功能,其功能表现为:当外设硬件处于准备好的状态时,硬件启动自动控制,而不需要软件再进行干预。

在串口这个外设的硬件流具体表现为:使用串口的RTS (Request to Send) 和CTS(Clear to Send) 针脚,当串口已经准备好接收新数据时,由硬件流自动把RTS针拉低(向外表示可接收数据);在发送数据前,由硬件流自动检查CTS针是否为低(表示是否可以发送数据),再进行发送。本串口例程没有使用到CTS和RTS,所以不采用硬件流控制。

https://www.sodocs.net/doc/402452638.html,ART_Mode = USART_Mode_Rx | USART_Mode_Tx;

配置串口的模式。为了配置双线全双工通讯,需要把Rx和Tx模式都开启。

7.填充完结构体,调用库函数USART_Init()向寄存器写入配置参数。

8.最后,调用USART_Cmd() 使能USART1外设。在使用外设时,不仅要使

能其时钟,还要调用此函数使能外设才可以正常使用。

1.4.5 printf()函数重定向

在main文件中,配置好串口之后,就通过下面的几行代码由串口往电脑里面的超级终端打印信息,打印的信息为一些字符串和当前的日期。

1.printf("\r\n this is a printf demo \r\n");

2.

3.printf("\r\n 欢迎使用野火M3实验板:) \r\n");

4.

https://www.sodocs.net/doc/402452638.html,ART1_printf(USART1, "\r\n This is a USART1_printf demo \r\n");

6.

https://www.sodocs.net/doc/402452638.html,ART1_printf(USART1, "\r\n ("__DATE__ " - " __TIME__ ") \r\n"); 下面是电脑超级终端的截图,从图可以看出程序是运行正确的。

调用这三个函数看似很简单,但在这三个函数的背后还得做些工作,我们先来看printf() 这个函数。要想printf()函数工作的话,我们需要把 printf() 重新定向到串口中。重定向,是指用户可以自己重写c的库函数,当连接器检查到用户编写了与C库函数相同名字的函数时,优先采用用户编写的函数,这样用户就可以实现对库的修改了。

为了实现重定向printf()函数,我们需要重写fputc() 这个c标准库函数,因为printf()在c标准库函数中实质是一个宏,最终是调用了fputc()这个函数的。

重定向的这部分工作,由usart.c文件中的fputc(int ch, FILE *f) 这个函数来完成,这个函数具体实现如下:

1./*

2. * 函数名:fputc

3. * 描述:重定向c库函数printf到USART1

4. * 输入:无

5. * 输出:无

6. * 调用:由printf调用

7. */

8.int fputc(int ch, FILE *f)

9.{

10./* 将Printf内容发往串口 */

11. USART_SendData(USART1, (unsigned char) ch);

12.// while (!(USART1->SR & USART_FLAG_TXE));

13.while( USART_GetFlagStatus(USART1,USART_FLAG_TC)!= SET);

14.return (ch);

15.}

这个代码中调用了两个ST库函数。USART_SendData() 和

USART_GetFlagStatus()其说明见图 1-8及图 1-9。

重定向时,我们把fputc()的形参ch,作为串口将要发送的数据,也就是说,当使用printf(),它调用这个fputc()函数时,然后使用ST 库的串口发送函数USART_SendData(),把数据转移到发送数据寄存器TDR,触发我们的串口向PC发送一个相应的数据。调用完USART_SendData()后,要使用

while( USART_GetFlagStatus(USART1,USART_FLAG_TC)!= SET) 语句不停地检查串口发送是否完成的标志位TC,一直检测到标志为完成,才进入一下步的操作,避免出错。在这段while的循环检测的延时中,串口外设已经由发送控制器根据我们的配置把数据从移位寄存器一位一位地通过串口线Tx发送出去了。

图 1-8 串口发送函数通过串口x发送一个数据

Data参数为将要发送的数据

检查USART的标志位

这些为可输入的标志位参数

如USART_FLAG_TXE表

示发送区是否为空的标志

位。

USART_FLAG_TC表示是

否发送完成的标志位

返回标志位检查结果。

图 1-9 串口标志位检查函数

在使用c标准输出库函数,要添加什么头文件?搞嵌入式的可不要把这个忘记了哦。在我们的main.c文件中要把 stdio.h这个头文件包含进来,还要在编译器中设置一个选项 Use MicroLIB (使用微库),见图 1-10。这个微库是keil MDK为嵌入式应用量身定做的C库,我们要先具有库,才能重定向吧?勾选使用之后,我们就可以使用printf() 这个函数了。

图 1-10 勾选使用微库

1.4.6 USART1_printf()函数

除了重定向的方法,我们还可以自己编写格式输入输出函数。

USART1_printf()便是一个完全自定义的格式输出函数,它的功能与重定向之后的printf类似。

让我们再来看看

USART1_printf(USART_TypeDef* USARTx, uint8_t *Data,...) 这个函数的实现,它调用了itoa(int value, char *string, int radix) 函数。关于这两个函数的具体实现请看usart.c中的源代码。这两个函数中有些变量是定义在stdarg.h 这个头文件中的,所以在usart.c中我们需要把这个头文件包含进来,这个头文件位于KDE的根目录下。我们可以在这个路径下找到它:

C:\Keil\ARM\RV31\INC。

1./*

2. * 函数名:itoa

3. * 描述:将整形数据转换成字符串

4. * 输入:-radix =10 表示10进制,其他结果为0

5. * -value 要转换的整形数

6. * -buf 转换后的字符串

7. * -radix = 10

8. * 输出:无

9. * 返回:无

10. * 调用:被USART1_printf()调用

11. */

12.static char *itoa(int value, char *string, int radix)

13.{

14.int i, d;

15.int flag = 0;

16.char *ptr = string;

17.

18./* This implementation only works for decimal numbers. */

19.if (radix != 10)

20. {

21. *ptr = 0;

22.return string;

23. }

24.

25.if (!value)

26. {

27. *ptr++ = 0x30;

28. *ptr = 0;

29.return string;

30. }

31.

32./* if this is a negative value insert the minus sign. */

33.if (value < 0)

34. {

35. *ptr++ = '-';

36./* Make the value positive. */

37. value *= -1;

38. }

39.for (i = 10000; i > 0; i /= 10)

40. {

41. d = value / i;

42.if (d || flag)

43. {

44. *ptr++ = (char)(d + 0x30);

45. value -= (d * i);

46. flag = 1;

47. }

48. }

49.

50./* Null terminate the string. */

51. *ptr = 0;

52.return string;

53.} /* NCL_Itoa */

54.

55./*

56. * 函数名:USART1_printf

57. * 描述:格式化输出,类似于C库中的printf,但这里没有用到C库

58. * 输入:-USARTx 串口通道,这里只用到了串口1,即USART1

59. * -Data 要发送到串口的内容的指针

60. * -... 其他参数

61. * 输出:无

62. * 返回:无

63. * 调用:外部调用

64. * 典型应用USART1_printf( USART1, "\r\n this is a demo \r\n" );

65. * USART1_printf( USART1, "\r\n %d \r\n", i );

66. * USART1_printf( USART1, "\r\n %s \r\n", j );

67. */

68.void USART1_printf(USART_TypeDef* USARTx, uint8_t *Data,...)

69.{

70.const char *s;

71.int d;

72.char buf[16];

73. va_list ap;

74. va_start(ap, Data);

75.while ( *Data != 0) // 判断是否到达字符串结束符

76. {

77.if ( *Data == 0x5c ) //'\'

78. {

79.switch ( *++Data )

80. {

81.case 'r': //回车符

82. USART_SendData(USARTx, 0x0d);

83. Data ++;

84.break;

85.

86.case 'n': //换行符

87. USART_SendData(USARTx, 0x0a);

88. Data ++;

89.break;

90.

91.default:

92. Data ++;

93.break;

94. }

95. }

96.else if ( *Data == '%')

97. { //

98.switch ( *++Data )

99. {

100.case 's': //字符串

101. s = va_arg(ap, const char *);

102.for ( ; *s; s++)

103. {

104. USART_SendData(USARTx,*s);

105.while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );

106. }

107. Data++;

108.break;

109.

110.case 'd': //十进制

111. d = va_arg(ap, int);

112. itoa(d, buf, 10);

113.for (s = buf; *s; s++)

114. {

115. USART_SendData(USARTx,*s);

116.while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );

117. }

118. Data++;

119.break;

120.default:

121. Data++;

122.break;

123. }

124. } /* end of else if */

125.else USART_SendData(USARTx, *Data++);

126.while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );

127. }

128.}

这部分代码有点多,在格式上编排不是很好,野火推荐大家直接看源码好点。

综上,我们已经可以用printf()和USART1_printf() 这两个函数来打印信息了,但到底用哪个比较好呢?其实各有千秋, printf() 函数会受缓冲区大小

的影响,有时候在用它打印的时候程序会发生莫名奇妙的错误,而实际上就是由于使用printf() 这个函数引起的,其优点就是这种情况很少见且支持的格式较多。而USART1_printf()则不会受缓冲区的影响产生莫名的错误,但其支持的格式较少。不过,相比之下,野火还是比较喜欢用USART1_printf()。

1.4.7 实验现象

将野火STM32开发板供电(DC5V),插上JLINK,插上串口线(两头都是母的交叉线,没有的话就DIY一个吧 ^_^),打开超级终端,配置超级终端为115200 8-N-1,将编译好的程序下载到开发板,即可看到超级终端打印出如下信息:

推荐-stm32中定时器产生不同PWM的基本思路 精品

在stm32中利用定时器TIM调制PWM的几种方法: 说说我的学习经历:从开始接触到现在有好几个月了,但是学习还是比较的费劲,而且速度也比较的缓慢,当然相比之前还是有很大的进步,记得刚刚学习的时候,建工程都是大四学长手把手教的。废话不多说先来讲讲定时器的配置: STM32F10系列最少3个、做多有8个定时器,都是16位定时器,且相互之间是独立的,计数范围为0x0000-0xffff,最大计数值为65535.可以用于测量输入信号的脉冲长度或者产生输出波形(输出比较和PWM)分为通用定时器,高级定时器,以及看门狗定时器 下面主要讲通用定时器的配置问题: 以定时器TIM1为例:先进行函数的配置 void timer1_config() { TIM_TimeBaseInitTypDef TIM_TimeBaseStructure; //开定时器1外设时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM1,ENABLE); //计时50000次时间为50000/10M=500ms TIM_TimeBaseStructure.TIM_Period=50000 ; TIM_TimeBaseStructure.TIM_Prescaler = 720-1;//720分频 TIM_TimeBaseStructure.TIM_ClockDivision =0;//时钟分割为0; //计数模式向上计数 TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; TIM_TimeBaseInit(TIM1,&TIM_TimeBaseStructure)//初始化TIM1 TIM_ITConfig(TIM1,TIM_IT_Update,ENABLE);//开启定时器中断 TIM_Cmd(TIM1,ENABLE); //使能定时器 } 关于时间的计算问题: 外设系统时钟的频率为72M,进行720分频以后,频率f=72M/720=100khz. 如果要定时0.1s 则计数值为10000,计算公式为:时间(t)=计数值(n)/频率(f).注意计数值n介于0到65535之间 有定时器则一定会有中断发生,所以要配置中断优先级,对于中断优先 级函数配置如下: V oid nvic_config() { NVIC_InitTypDef NVIC_InitStructure; //抢占优先级为1位,从优先级为3位 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1) ; NVIC_InitStructure.NVIC_IRQChannel=TIM1_IRQn; //定义定时器1为请求通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0; //抢占式优先级为0 NVIC_InitStructure.NVIC_IRQChannelSubPriority=2; //从优先级为2 NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //使能中断优先级 NVIC_Init(&NVIC_InitStructure); //初始化中断 } 对于优先级中的抢占式和从优先级做如下解释: 抢占式优先级:是可以抢占的中断,比如正在执行的优先级为10的中断,突然来了一个优

正确认识纯化水系统的“死角

正确认识系统的“死角” 死角检查是系统进行安装确认(IQ)时的一项重要内容。在制药流体工艺系统(如制药用水系统、制药工艺配液系统、CIP/SIP系统)中,任何死角的存在均可能导致整个系统的污染。 死角过大所带来的风险主要如下: ?为微生物繁殖提供了“温床”并导致“生物膜”的形成,引起微生物指标、TOC指标或内毒素指标超标,导致水质指标不符合药典要求; ?系统消毒或灭菌不彻底导致的二次微生物污染; ?系统清洗不彻底导致的二次颗粒物污染或产品交叉污染。 因此,中国2010版GMP要求“管道的设计和安装应避免死角、盲管”。 《美国机械工程师协会生物加工设备标准》2014版对于死角有准确的定义,《ASMEBPE》(2014)规定:“死角”是指当管路或容器使用时,能导致产品污染

的区域(deadleg: an area of entrapment in a vessel or piping run that could lead tocontamination of the product.)。 1976年,美国FDA在CFR212法规上第一次采用量化方法进行死角的质量管理,工程上俗称“6D”规则,其含义为“当L/d<6时,证明此处无死角”,其中L指“流动侧主管网中心到支路盲板(或用点阀门中心)的距离”,d为支路的直径。随后的研究表明,“3D”规则更符合洁净流体工艺系统的微生物控制要求,其中L的含义变更为“流动侧主管网管壁到支路盲板(或用点阀门中心)的距离”(图1)。 图1 死角的发展 更加准确的死角量化定义来自于《ASMEBPE》规范(图2),该定义明确规定:L是指“流动侧主管网内壁到支路盲板(或用点阀门中心)的距离”,D是指“非流动侧支路管道的内径”。

STM32高级定时器日记之PWM

STM32高级定时器PWM实用程序 文章来源:本站原创作者:佚名 该文章讲述了STM32高级定时器PWM实用程序. 高级定时器与通用定时器比较类似,下面是一个TIM1 的PWM 程序,TIM1是STM32唯一的高级定时器。共有4个通道有死区有互补。 先是配置IO脚: GPIO_InitTypeDef GPIO_InitStructure; /* PA8设置为功能脚(PWM) */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); /*PB13 设置为PWM的反极性输出*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOB, &GPIO_InitStructure); /*开时钟PWM的与GPIO的*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); /*配置TIM1*/ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; void Tim1_Configuration(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; TIM_DeInit(TIM1); //重设为缺省值 /*TIM1时钟配置*/ TIM_TimeBaseStructure.TIM_Prescaler = 4000; //预分频(时钟分 频)72M/4000=18K TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数TIM_TimeBaseStructure.TIM_Period = 144; //装载值18k/144=125hz 就是说向上加的144便满了 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置了时钟分割不

管道死角盲管规则

6D、3D、2D、管道死角/盲管规则,以及零死角阀门介绍 6D、3D、2D、管道死角/盲管规则以及零死角阀门介绍 关于管道死角/盲管的定义和要求,不同法规和指南有不同的要求,这些要求如下: 1、1976 CFR 212规范为6D,指主管道中心到支管阀门中心的距离应小于支管直径的6倍。 2、1993 美国高纯水检查指南为6D,指主管道中心到支管阀门密封点的长度应小于支管直径的6倍。 3、2001 ISPE水和蒸汽基准指南为3D,指主管外壁到支管阀门密封点的长度应小于支管直径的3倍。 4、2009 ASME BPE为2D,指主管内壁到阀门密封点的长度应小于支管直径的2倍。 5、WHO 建议为,应避免大于分支管径倍的盲管。 6、2010版中国GMP指南: 为了避免将来造成混乱,本指南建议死角长度从管的外壁来考虑。我们建议避免对于最大可允许的死角做硬性规定。 最后,在不考虑死角长度的情况下,水质必须满足要求。工程设计规范要求死角长度最小,有很多好的仪表和阀门的设计是尽量减少死角的。 我们应该认识到如果不经常冲洗或消毒,任何系统都能会存在死角。各种规定和提法甚至测量的方法不尽相同,但是目前的所有提法都不是“法规”而是工程的建议和标准。TheTruth about the 3D/6D Rule 3D/6D规则的真相 The installation of pipework leads to recurrent discussions about how deadlegs can be prevented and about the maximum length outgoing pipes/pipe tees mayhave for the sensor. There is less throughflow in dead legs. Hence, it isharder to clean them and during thermal sanitisation it takes longer until these"branches" have also reached the required temperature. In calls fortender and tests the 3D/6D rule is often used for the specification, but notalways in the completely correct way. In order to further explain this, pleaseread following the history of this rule. 管道的安装一再引起关于如何防止死管的讨论,以及探头安装位置连出的支管最大长度问题的讨论。在死管中水流较少,因此很难对其进行清洁,并且在高温消毒中会需要很长时间使得这些“支管”也能达到所需的温度。在设计和测试中,3D/6D规则通常用作标准,但并不总是用的完全正确。为了进一步解释这个问题,请阅读以下关于此规则的历史。The rule for the prevention of dead legs (in a WFI system) is mentionedfor the first time in the draft of the FDA Guides for Large VolumeParenterals (LVP), 21 CFR in 1972.

stm32高级定时器使用教程

STM32 高级定时器-PWM简单使用 2010-04-14 14:49:29| 分类:STM32 | 标签:|举报|字号大中小订阅高级定时器与通用定时器比较类似,下面是一个TIM1 的PWM 程序,TIM1是STM32唯一的高级定时器。共有4个通道有死区有互补。 先是配置IO脚: GPIO_InitTypeDef GPIO_InitStructure; /* PA8设置为功能脚(PWM) */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); /*PB13 设置为PWM的反极性输出*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); /*开时钟PWM的与GPIO的*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); /*配置TIM1*/ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure;

管道死角盲管规则

6D、3D、2D、1.5D管道死角/盲管规则,以及零死角阀门介绍 6D、3D、2D、1.5D管道死角/盲管规则以及零死角阀门介绍 关于管道死角/盲管的定义和要求,不同法规和指南有不同的要求,这些要求如下: 1、1976 CFR 212规范为6D,指主管道中心到支管阀门中心的距离应小于支管直径的6倍。 2、1993 美国高纯水检查指南为6D,指主管道中心到支管阀门密封点的长度应小于支管直径的6倍。 3、2001 ISPE水和蒸汽基准指南为3D,指主管外壁到支管阀门密封点的长度应小于支管直径的3倍。 4、2009 ASME BPE为2D,指主管内壁到阀门密封点的长度应小于支管直径的2倍。 5、WHO 建议为1.5D,应避免大于分支管径1.5倍的盲管。 6、2010版中国GMP指南: 为了避免将来造成混乱,本指南建议死角长度从管的外壁来考虑。我们建议避免对于最大可允许的死角做硬性规定。 最后,在不考虑死角长度的情况下,水质必须满足要求。工程设计规范要求死角长度最小,有很多好的仪表和阀门的设计是尽量减少死角的。

我们应该认识到如果不经常冲洗或消毒,任何系统都能会存在死角。各种规定和提法甚至测量的方法不尽相同,但是目前的所有提法都不是“法规”而是工程的建议和标准。 TheTruth about the 3D/6D Rule 3D/6D规则的真相 The installation of pipework leads to recurrent discussions about how deadlegs can be prevented and about the maximum length outgoing pipes/pipe tees mayhave for the sensor. There is less throughflow in dead legs. Hence, it isharder to clean them and during thermal sanitisation it takes longer until these"branches" have also reached the required temperature. In

STM32的PWM精讲

STM32的PWM精讲 通过对TM1定时器进行控制,使之各通道输出插入死区的互补PWM输出,各通道输出频率均为17.57KHz。其中,通道1输出的占空比为50%,通道2输出的占空比为25%,通道3输出的占空比为12.5%。各通道互补输出为反相输出。 TM1定时器的通道1到4的输出分别对应PA.08、PA.09、PA.10和PA.11 引脚,而通道1到3的互补输出分别对应PB.13、PB.14和PB.15引脚,中止输入引脚为PB.12。将这些引脚分别接入示波器,在示波器上观查相应通道占空比的方波[12]。 配置好各通道后,编译运行工程;点击MDK的Debug菜单,点击Start/Stop Debug Session;通过示波器察看 PA.08、PA.09、PA.10、PB.13、PB.14、PB.15 的输出波形,其中PA.08和PB.13为第一通道和互补通道,PB.09和PB.14为第二通道和其互补通道,PB.10和PB.15为第三通道和其互补通道;第一通道显示占空比为50%,第二通道占空比为25%,第三通道占空比为12.5%。 第2章STM32处理器概述 STM32F103xx增强型系列产品中内置了多达3个同步的标准定时器。每个定时器都有一个16位的自动加载递加/递减计数器、一个16位的预分频器和4个独立的通道,每个通道都可用于输入捕获、输出比较、PWM和单脉冲模式输出,在最大的封装配置中可提供最多12个输入捕获、输出比较或PWM通道。它们还能通过定时器链接功能与高级控制定时器共同工作,提供同步或事件链接功能。 在调试模式下,计数器可以被冻结。任一个标准定时器都能用于产生PWM 输出。每个定时器都有独立的DMA请求机制。 2.4.2 高级控制定时器[22] 高级控制定时器(TM1)由一个16位的自动装载计数器组成,它由一个可编程预分频器驱动。它适合多种用途,包含测量输入信号的脉冲宽度(输入捕获),或者产生输出波形(输出比较,PWM,嵌入死区时间的互补PWM等)。 使用定时器预分频器和RCC时钟控制预分频器,可以实现脉冲宽度和波形周期从几个微秒至几个毫秒的调节。高级控制(TIM1)和通用(TMx)定时器是完全

零死角玩转stm32-高级篇5、3.2寸液晶显示(中英文+图片)

0、友情提示 《零死角玩转STM32》系列教程由初级篇、中级篇、高级篇、系统篇、四个部分组成,根据野火STM32开发板旧版教程升级而来,且经过重新深入编写,重新排版,更适合初学者,步步为营,从入门到精通,从裸奔到系统,让您零死角玩转STM32。M3的世界,与野火同行,乐意惬无边。 另外,野火团队历时一年精心打造的《STM32库开发实战指南》将于今年10月份由机械工业出版社出版,该书的排版更适于纸质书本阅读以及更有利于查阅资料。内容上会给你带来更多的惊喜。是一本学习STM32必备的工具书。敬请期待!

5、液晶显示(中、英、Pic ) 5.1 实验简介 在《液晶触摸画板》中,我们已经成功地实现了驱动LCD 和触摸屏,并制作了触摸画板小应用,但是若要显示文字或图片文件,则还需要利用文件系统,读取保存在SD 卡中的字库文件、图片文件。 5.2 什么是字模 我们知道其实液晶屏就是一个由像素点组成的点阵,若要显示文字,则需要很多像素点的共同构成。见下错误!未找到引用源。,图中是两个由16*16的点阵显示的两个汉字。 如果我们规定:每个汉字都由这样16*16的点阵来显示,把笔迹经过的像 素点以“1”表示,没有笔迹的点以“0”表示,每个像素点的状态以一个二进制位来记录,用16*16/8 =32个字节就可以把这个字记录下来。这32个字节数据就称为该文字的字模,还有其它常用字模是24*24、32*32的。16*16的“字”的字模数据为: 1. /* 字 */ 2. unsigned char code Bmp003[]= 3. { 4. /*------------------------------------------------------------ 5. ; 源文件 / 文字 : 字 6. ; 宽×高(像素): 16×16 7. ; 字模格式/大小 : 单色点阵液晶字模,横向取模,字节正序/32字节 8. ----------------------------------------------------------*/ 9. Bit7 ~Bit0 B it7 ~Bit0 每个字16x2Byte

STM32学习笔记通用定时器PWM输出

STM32学习笔记(5):通用定时器PWM输出 2011年3月30日TIMER输出PWM 1.TIMER输出PWM基本概念 脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽度的控制。一般用来控制步进电机的速度等等。 STM32的定时器除了TIM6和TIM7之外,其他的定时器都可以用来产生PWM输出,其中高级定时器TIM1和TIM8可以同时产生7路的PWM输出,而通用定时器也能同时产生4路的PWM输出。 1.1PWM输出模式 STM32的PWM输出有两种模式,模式1和模式2,由TIMx_CCMRx寄存器中的OCxM位确定的(“110”为模式1,“111”为模式2)。模式1和模式2的区别如下: 110:PWM模式1-在向上计数时,一旦TIMx_CNTTIMx_CCR1时通道1为无效电平(OC1REF=0),否则为有效电平(OC1REF=1)。 111:PWM模式2-在向上计数时,一旦TIMx_CNTTIMx_CCR1时通道1为有效电平,否则为无效电平。 由此看来,模式1和模式2正好互补,互为相反,所以在运用起来差别也并不太大。 而从计数模式上来看,PWM也和TIMx在作定时器时一样,也有向上计数模式、向下计数模式和中心对齐模式,关于3种模式的具体资料,可以查看《STM32参考手册》的“14.3.9 PWM模式”一节,在此就不详细赘述了。 1.2PWM输出管脚 PWM的输出管脚是确定好的,具体的引脚功能可以查看《STM32参考手册》的“8.3.7 定时器复用功能重映射”一节。在此需要强调的是,不同的TIMx有分配不同的引脚,但是考虑到管脚复用功能,STM32提出了一个重映像的概念,就是说通过设置某一些相关的寄存器,来使得在其他非原始指定的管脚上也能输出PWM。但是这些重映像的管脚也是由参考手册给出的。比如

STM32 高级定时器-PWM简单使用

STM32 高级定时器-PWM简 单使用 高级定时器与通用定时器比较类似,下面是一个TIM1 的PWM 程序,TIM1是STM32唯一的高级定时器。共有4个通道有死区有互补。 先是配置IO脚: GPIO_InitTypeDef GPIO_InitStructure; /* PA8设置为功能脚(PWM) */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); /*PB13 设置为PWM的反极性输出*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); /*开时钟PWM的与GPIO的*/ RCC_APB2PeriphClockCmd(RCC_A PB2Periph_TIM1,ENABLE); RCC_APB2PeriphClockCmd(RCC_A PB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_A PB2Periph_GPIOB, ENABLE); /*配置TIM1*/ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; void Tim1_Configuration(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; TIM_DeInit(TIM1); //重设为缺省值 /*TIM1时钟配置*/ TIM_TimeBaseStructure.TIM_Prescaler = 4000; //预分频(时钟分频)72M/4000=18K TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数 TIM_TimeBaseStructure.TIM_Period = 144; //装载值18k/144=125hz 就是说向上加的144便满了 TIM_TimeBaseStructure.TIM_ClockDivision =

STM32-PWM输出总结讲课讲稿

学习后发现stm32的定时器功能确实很强大,小总结一下方便以后使用的时候做参考。Stm32定时器一共分为三种:tim1和tim8是高级定时器,6和7是基本定时器,2—5是通用定时器。从名字就可以看得出来主要功能上的差异。今天我主要是用定时器做pwm输出,所以总结也主要是针对pwm方面的。 先大致说下通用和高级定时器的区别。通用的可以输出四路pwm信号互不影响。高级定时器可以输出三对互补pwm信号外加ch4通道,也就是一共七路。 所以这样算下来stm32一共可以生成4*5+7*2=30路pwm信号。接下来还有功能上的区别:通用定时器的pwm信号比较简单,就是普通的调节占空比调节频率(别的不常用到的没去深究);高级定时器的还带有互补输出功能,同时互补信号可以插入死区,也可以使能刹车功能,从这些看来高级定时器的pwm天生就是用来控制电机的。 Pwm输出最基本的调节就是频率和占空比。频率当然又和时钟信号扯上了关系。高级定时器是挂接到APB2上,而通用定时器是挂接到APB1上的。APB1和APB2的区别就要在于时钟频率不同。APB2最高频率允许72MH,而APB1最高频率为36MHZ。这样是不是通用定时器只最高36MHZ频率呢,不是的;通用定时器时钟信号完整的路线应该是下面这样的:AHB(72mhz)→APB1分频器(默认2)→APB1时钟信号(36mhz)→倍频器(*2倍)→通用定时器时钟信号(72mhz)。 在APB1和定时器中间的倍频器起到了巨大的作用,假如红色字体的“APB1分频器”假如不为1(默认是2),倍频器会自动将APB1时钟频率扩大2倍后作为定时器信号源,这个它内部自动控制的不用配置。设置这个倍频器的目的很简单就是在APB1是36mhz的情况下通用定时器的频率同样能达到72mhz。我用的库函数直接调用函数SystemInit(); 这个函数之后时钟配置好了:通用定时器和高级定时器的时钟现在都是72mhz(你也可以自己再配置一下RCC让他的频率更低,但是不能再高了)。定时器接下来还有一个分频寄存器:TIMX_PSC 经过他的分频后,才是定时器计数的频率。所以真正的时钟频率应该是72mhz/(TIMX_PSC-1),我们设为tim_frepuency下面还会用到。 stm32的时钟频率弄得确实是很饶人的,所以关键就是先要把思路理清楚。时钟的频率弄好了下面终于可以开说重点PWM了。当然还少不了频率:pwm主要就是控制频率和占空比的:这两个因素分别通过两个寄存器控制:TIMX_ARR和TIMX_CCRX。ARR寄存器就是自动重装寄存器,也就是计数器记到这个数以后清零再开始计,这样pwm的频率就是tim_frequency/(TIMX_ARR-1)。在计数时会不停的和CCRX寄存器中的数据进行比较,如果小于的话是高电平或者低电平,计数值大于CCRX值的话电平极性反相。所以这也就控制了占空比。 下面是定时器1的配置代码: GPIO_InitTypeDef GPIO_InitStructure2; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; TIM_BDTRInitTypeDef TIM_BDTRInitStructure; //第一步:配置时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA| RCC_APB2Periph_GPIOB|RCC_APB2Periph_TIM1 ,ENABLE); //第二步,配置goio口 /********TIM1_CH1 引脚配置*********/

stm32f103通用定时器pwm应用例程--蜂鸣器演奏乐曲

stm32f103通用定时器pwm应用例程--蜂鸣器演奏乐曲STM32F103通用定时器PWM应用例程:蜂鸣器演奏乐曲一(说明:本例程是将流明LM3SLib_Timer.pdf文档中的例程9及例程10(PWM应用: 蜂鸣器演奏乐曲),移植到STM32F103上。 二(流明LM3SLib_Timer.pdf例程9及例程10的拷贝: 例程9( Timer PWM应用:蜂鸣器发声 如图1.1所示,为EasyARM1138开发板上的蜂鸣器驱动电路。蜂鸣器类型是交流蜂鸣器,也称无源蜂鸣器,需要输入一列方波才能鸣响,发声频率等于驱动方波的频率。 图1.1 蜂鸣器驱动电路 程序清单1.9是Timer模块16位PWM模式的一个应用,可以驱动交流蜂鸣器发声,运行后蜂鸣器以不同的频率叫两声。其中"buzzer.h"和"buzzer.c"是蜂鸣器的驱动程序,仅有3个驱动函数,用起来很简捷。

程序清单1.9 Timer PWM应用:蜂鸣器发声 文件:main.c #include "systemInit.h" #include "buzzer.h" // 主函数(程序入口) int main(void) { jtagWait(); // 防止JTAG失效,重要~ clockInit(); // 时钟初始化:晶振,6MHz buzzerInit(); // 蜂鸣器初始化 buzzerSound(1500); // 蜂鸣器发出1500Hz声音 SysCtlDelay(400* (TheSysClock / 3000)); // 延时约400ms buzzerSound(2000); // 蜂鸣器发出2000Hz声音 SysCtlDelay(800* (TheSysClock / 3000)); // 延时约800ms buzzerQuiet( ); // 蜂鸣器静音 for (;;) { } } 文件:buzzer.h #ifndef __BUZZER_H__ #define __BUZZER_H__ // 蜂鸣器初始化 extern void buzzerInit(void); // 蜂鸣器发出指定频率的声音 extern void buzzerSound(unsigned short usFreq); // 蜂鸣器停止发声

2020申论真题精讲十六

2020申论真题精讲十六 来自:申论资料 人须在事上磨 2019年11月28日20:11 2020年国家公务员考试申论真题(地市级)第五题 参考答案 近期,S省开展乡村治理先进典型评选工作。M市打算推荐莱康村参加评选,如果你是该市有关部门的工作人员,请根据“给定资料5”写一份莱康村参评的推荐材料。(30分) 要求: (1) 紧扣资料,内容全面; (2)逻辑清晰,语言准确; (3)字数800~ 1000字。 题干解析:

?近期,S省开展乡村治理先进典型评选工作=这句话非常重要,给出了我们这次写作的中心主题,那就是乡村治理,也就是我们写的标题里一定要出现这四个字,才是最安全的。这里涉及这个题要不要写标题和如何写标题的问题,这个一定要写标题,我之前反复说过,应用文啥都可以不写,但必须写标题,这个一定要记住。那么标题怎么写呢?可以直接像作文一样起个标题,比如:用乡村治理托起和谐莱康。你也可以写个关于莱康村参评乡村治理先进典型的推荐材料。一般来说,这种典型案例的推荐肯定不是公文,标题拟定相对灵活,并且推荐典型要有一定文学加工,也就是需要添油加醋的忽悠,所以最好起一个类似于作文的标题,让评委一看标题就想把他塑造成典型,我曾经看过某单位报的典型案例,标题叫做什么什么的“五环”,具体标题我忘了,但对于“五环”印象很深了,当时我还还好奇,为什么叫“五环”,原来人家的案例里五个做法,采取“零失误、零等待、零死角、零容忍、零距离”五个措施,所以标题还是要起的生动点。 ?M市打算推荐莱康村参加评选,如果你是该市有关部门的工作人员,请根据“给定资料5”写一份莱康村参评的推荐材料=这里就告诉我们要根据材料5写个参评材料,别的就是你写出来莱康村基本情况,他干了什么,达到一个什么效果,要么为嘛推荐呢,就是说写个应用文。这时有个问题,要不要落款和写日期,要是写过类似材料的肯定知道,一定要落款和写日期,

STM32 定时器 PWM

STM32+按键调控PWM输出+串口输出占空比(2010-09-20 21:59) 分类:STM32学习笔记

TIM.c

main.c

#include "stm32f10x.h" TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; uint16_t CCR1_V al = 250; uint16_t CCR2_V al = 500; uint16_t CCR3_V al = 750; uint16_t CCR4_V al = 0; uint16_t PrescalerV alue = 0; void RCC_Configuration(void); //时钟配置 void GPIO_Configuration(void); //管脚配置 void Delay (__IO uint32_t nCount); //延时函数 int main(void) { RCC_Configuration(); GPIO_Configuration(); PrescalerV alue = (uint16_t) (SystemCoreClock / 24000000) - 1; TIM_TimeBaseStructure.TIM_Period = 1000; //周期 TIM_TimeBaseStructure.TIM_Prescaler = PrescalerV alue; //分频 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //时钟分割 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数模式 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //初始TIM3 /*************************** 通道1 ********************************/ TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //PWM2 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //PWM功能使能TIM_OCInitStructure.TIM_Pulse = CCR1_V al; //写比较值(占空比TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //置高 TIM_OC1Init(TIM3, &TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); /****************************** 通道2 ******************************/ /* PWM1 Mode configuration: Channel2 */ TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = CCR2_V al; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC2Init(TIM3, &TIM_OCInitStructure); TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); /******************************* 通道3 *********************************/ /* PWM1 Mode configuration: Channel3 */

基于寄存器操作的STM32高级定时器TIM1的四路PWM输出程序讲解

经过一天的努力,终于把stm32 tim1的四路pwm输出搞了出来,为了使大家快速的用起tim1,打算写这篇文档与大家分享。 stm32 tim1功能丰富。针对pwm输出与tim2只有细小的差别,之前在网上找了一些网友的程序,发现大部分都是基于库文件写的,不能对tim1的pwm输出有深层次的理解,个人认为一个合格的程序员,想要最大程度的用好一个片子的话还是要针对寄存器直接操作,完全了解定时器的运行过程,可以对片子的结构有一定的了解。 高级控制定时器(TIM1 和TIM8) 由一个16位的自动装载计数器组成,它由一个可编程的预分频器驱动。它适合多种用途,包含测量输入信号的脉冲宽度( 输入捕获) ,或者产生输出波形( 输出比较、PWM、嵌入死区时间的互补PWM等)。使用定时器预分频器和RCC 时钟控制预分频器,可以实现脉冲宽度和波形周期从几个微秒到几个毫秒的调节。 高级控制定时器(TIM1 和TIM8) 和通用定时器(TIMx) 是完全独立的,它们不共享任何资源。 请读者仔细阅读一下信息: 脉冲宽度调制模式可以产生一个由TIMx_ARR 寄存器确定频率、由TIMx_CCRx寄存器确定占空比的信号。在TIMx_CCMRx寄存器中的OCxM 位写入’110’(PWM 模式1) 或’111’(PWM 模式2) ,能够独立地设置每个OCx 输出通道产生一路PWM。必须通过设置TIMx_CCMRx寄存器的OCxPE位使能相应的预装载寄存器,最后还要设置TIMx_CR1 寄存器的ARPE 位,( 在向上计数或中心对称模式中)使能自动重装载的预装载寄存器。仅当发生一个更新事件的时候,预装载寄存器才能被传送到影子寄存器,因此在计数器开始计数之前,必须通过设置TIMx_EGR 寄存器中的UG位来初始化所有的寄存器。OCx 的极性可以通过软件在TIMx_CCER 寄存器中的CCxP位设置,它可以设置为高电平有效或低电平有效。OCx 的输出使能通过(TIMx_CCER 和TIMx_BDTR寄存器中)CCxE 、CCxNE、MOE、OSSI和OSSR 位的组合控制。详见TIMx_CCER寄存器的描述。在PWM模式(模式1或模式2) 下,TIMx_CNT 和TIMx_CCRx始终在进行比较,( 依据计数器的计数方向)以确定是否符合TIMx_CCRx≤TIMx_CNT 或者TIMx_CNT ≤TIMx_CCRx。根据TIMx_CR1 寄存器中CMS位的状态,定时器能够产生边沿对齐的PWM信号或中央对齐的PWM信号。 根据以上信息的提示,我们可以归纳出寄存器操作的步骤: (1)使能相应的定时器时钟线。 (2)配置tim1四路pwm输出引脚为复用功能输出。 (3)配置tim1的分频比和计数装载值。 (4)配置相应通道的pwm输出模式和使能预装载。 (5)打开自动重装载预装载允许位。 (6)根据需要选择向上或者是向下计数模式。 (7)打开相应通道的输出使能。 (8)配置各通道的占空比。 (9)开启开启OC和OCN。 (10)最后使能定时器1. 关于分频比和计数装载值的选择: Tim1的最大时钟频率为72M,假设我们想得到一个频率为4KHZ,占空比0~100%步进为1的pwm。根据计数器的时钟频率(CK_CNT) 等于f CK_PSC/( PSC[15:0]+1)。因为占空比0~100%步进为1,所以我们把计数值设为100.比较值可以设为0~100之间,实现占空比的调节。计数器的时钟频率(CK_CNT)应为4KHZ X 100=400KHZ。所以PSC = 179。ARR=100。有不明白的请看手册。

STM32 定时器与 PWM 快速使用入门

STM32 定时器与 PWM 快速使用入门 要求:在万利的开发板 EK-STM32F 上产生周期为1秒,占空比分别为 50% 10%的 PWM 并且点亮板上的 LD1,LD2 灯闪烁。 做法很简单。 STM32的PWM是由定时器来产生的。 可以看出。定时器3的通道1至4在GPIO端口的映像。如果是完全映射。 各通道的连接引脚如下: CH1=PC6, CH2=PC7, CH3=PC8, CH4=PC9 这样,刚好与板上的LD1,LD2灯符合,因为LD1连接到PC7,LD2连接到PC6引脚。 关于PWM一些知识. STM32的TIMx 是 TIMx_ARR 寄存器确定频率(周期)、由TIMx_CCRx 寄存器确定占空比的信号。 使用定时器3。而TIM2、3、4的时钟源是 APB1 即是 PCLK1 ( APB1 对应 PCLK1 ) PCLK1 = APB1 = HCLK/2 = SYSCLK/2 = 36MHZ (36,000,000 HZ) 但是注意:倍频器会自动倍2,即是【72MHZ】! 代码如下: void STM32_PWM_GPIO_Configuration(void) { // 11:完全映像 STM32_Afio_Regs->mapr.bit.TIM3_REMAP=3; // LD1 =P7 LD2=PC6 /*GPIOA Configuration: ( PC6 PC7 ) TIM3 channel 1 and 2 as alternate function push -pull */ STM32_Gpioc_Regs-&https://www.sodocs.net/doc/402452638.html,F6=Output_Af_push_pull; // PC.06 复用功能推挽输出模式 STM32_Gpioc_Regs->crl.bit.MODE6=Output_Mode_50mhz; // PC.06 输出模式,最大速度50MHz STM32_Gpioc_Regs-&https://www.sodocs.net/doc/402452638.html,F7=Output_Af_push_pull; // PC.07 复用功能推挽输出模式 STM32_Gpioc_Regs->crl.bit.MODE7=Output_Mode_50mhz; // PC.07 输出模式,最大速度50MHz

相关主题