您当前的位置:五五电子网电子知识电子学习基础知识电脑-单片机-自动控制STM32单片机基础学习:从勉强看懂一行程序到IO口研究 正文
STM32单片机基础学习:从勉强看懂一行程序到IO口研究

STM32单片机基础学习:从勉强看懂一行程序到IO口研究

点击数:7210 次   录入时间:03-04 11:37:43   整理:http://www.55dianzi.com   电脑-单片机-自动控制
  /* Enable Prefetch Buffer */
  FLASH-》ACR |= FLASH_ACR_PRFTBE;
  /* Flash 2 wait state */
  FLASH-》ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
  FLASH-》ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
  /* HCLK = SYSCLK */
  RCC-》CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
  //找到定义: #define RCC_CFGR_HPRE_DIV1 ((uint32_t)0x00000000) /*!《 SYSCLK not divided */
  /* PCLK2 = HCLK */
  RCC-》CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
  //找到定义:#define RCC_CFGR_PPRE2_DIV1 ((uint32_t)0x00000000) /*!《 HCLK not divided */
  /* PCLK1 = HCLK */
  RCC-》CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
  //找到定义:#define RCC_CFGR_PPRE1_DIV2 ((uint32_t)0x00000400) /*!《 HCLK divided by 2 */
  #ifdef STM32F10X_CL
  ……
  #else
  /* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
  RCC-》CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
  RCC_CFGR_PLLMULL));
  RCC-》CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
  #endif /* STM32F10X_CL */
  //以上是设定PLL的倍频系数为9,也就是说,这个72M是在外部晶振为8M时得到的。
  /* Enable PLL */
  RCC-》CR |= RCC_CR_PLLON;
  /* Wait till PLL is ready */
  while((RCC-》CR & RCC_CR_PLLRDY) == 0)
  {
  }
  /* Select PLL as system clock source */
  RCC-》CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  RCC-》CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
  /* Wait till PLL is used as system clock source */
  while ((RCC-》CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
  {
  }
  }
  else
  { /* If HSE fails to start-up, the application will have wrong clock
  configuration. User CAN add here some code to deal with this error */
  /* Go to infinite loop */
  while (1)
  {
  }
  }
  }

  至此,我们可以归纳几条:
  (1) 时钟源有3个
  (2) 开机时默认是HSI起作用,可以配置为所要求的任意一个时钟
  (3) 配置时必须按一定的顺序来打开或都关闭一些位,并且各时钟起作用有一定的时间,因此要利用芯片内部的标志位来判断是否可以执行下一步。
  (4) 如果外部时钟、PLL输出失效,系统可以自动回复到HSI(开启时钟安全系统)
  (5) HSI的频率准确度可以达到+/- 1%,如果有必要时,还可以用程序来调整这个频率,可调的范围大致在200KHz左右。
  最后让我们来感受一下劳动的果实吧--试着改改频率看有何反应。
  为查看更改后的效果,先记录更改前的数据。将调试切换到仿真,在第一条:
  Delay(0xAFFFF);
  指令执行前后,分别记录下Status和Sec
  Status:2507 3606995
  Sec:0.00022749 0.05028982
  将振荡频率更改为36MHz,即
  。。.
  #define SYSCLK_FREQ_36MHz 36000000 //去掉该行的注释
  /* #define SYSCLK_FREQ_48MHz 48000000 */
  /* #define SYSCLK_FREQ_56MHz 56000000 */
  /*#define SYSCLK_FREQ_72MHz 72000000*/ //将该行加上注释
  再次运行,结果如下:
  Status:2506 3606994
  Sec:0.00008478 0.10036276
  基本上是延时时间长了一倍。改成硬件仿真,将代码写入板子,可以看到LED闪烁的频率明显变慢了。

IO研究

  前面的例子研究了时钟,接下来就来了解一下引脚的情况
  Main.c中,有关I/O口的配置代码如下:
  void GPIO_Configuration(void)
  {
  GPIO_InitTypeDef GPIO_InitStructure;
  /* Configure IO connected to LD1, LD2, LD3 and LD4 leds *********************/
  GPIO_InitStructure.GPIO_PIN = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOD, &GPIO_InitStructure);
  这几行代码是将GPIOD的第8,9,10和11引脚配置成输出,并且还可以设定输出引脚的速度(驱动能力?),这里设定为 50MHz,这应该是常用的,还有可以设置为2MHz的。那么如何将引脚设置成输入呢?查看电路原理图,GPIOD.0~GPIO.4是接一个摇杆的5个按钮的,因此,下面尝试着将它们设置成为输入端。
  GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOD, &GPIO_InitStructure);
  第1行和第3行完全是照抄,第2行那个GPIO_Mode_IN_FLOATING是在stm32f10x_gpio.h中找到的。
  
  当然是因为这里还有GPIO_Mode_Out_PP,所以猜测应该是它了。至于还有其他那么多的符号就不管了。
  定义完成,编译完全通过,那就接下来准备完成下面的代码了。
  int main(void)
  {
  Init_All_Periph();
  while(1)
  { if( GPIO_ReadInputDataBit(GPIOD,GPIO_Pin_0)) //1
  { GPIO_ResetBits(GPIOD, GPIO_Pin_8);
  }
  else
  { /* Turn on LD1 */
  GPIO_SetBits(GPIOD, GPIO_Pin_8);
  /* Insert delay */
  }
  。。.。。.
  标号为1的行显然其作用是判断GPIOD.0引脚是0还是1。这个函数是在stm32f10x_gpio.c中找到的。
  uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
  {
  uint8_t bitstatus = 0x00;
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GET_GPIO_PIN(GPIO_Pin));
  if ((GPIOx-》IDR & GPIO_Pin) != (uint32_t)Bit_RESET)
  {
  bitstatus = (uint8_t)Bit_SET;
  }
  else
  {
  bitstatus = (uint8_t)Bit_RESET;
  }
  return bitstatus;
  }
  虽然程序还有很多符号看不懂(没有去查),但凭感觉它应该是对某一个引脚的状态进行判断,因为这个函数的类型是uint8_t,估计stm32没有bit型函数(需要验证),所以就用了uint8_t型了),如果是读的端口的值,应该用uint16_t型。这一点在下面也可以得到部分的验证:
  uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx)
  uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx)
  这些函数是读引脚及输出寄存器的数据的。

 再次编译,也是顺利通过,依法炮制,将其他三个引脚输入控制LED的代码也写上,为保险起见,先用软件仿真,免得反复擦写FLASH(顺便说一句,目前还没有搞定将代码写入RAM及从RAM中执行)
  点击看大图
  进入仿真后打开外围部件接口,单步执行,果然如同设想那样运作了,单击Pins 0后面的勾,再次运行,果然PIN8后面的勾没了。做到这里,就感觉到用keil的好处了,这块熟啊,几乎没有花时间在上面,一用就成了。



www.55dianzi.com


  至此,按我的习惯,要翻开STM32F的数据手册,研究一下其IO端口了。下面是数据手册中的一段话:
  -------------------------------------
  每个GPI/O端口有两个32位配置寄存器(GPIOx_CRL,GPIOx_CRH),两个32位数据寄存器(GPIOx_IDR,GPIOx_ODR),一个32位置位/复位寄存器(GPIOx_BSRR),一个16位复位寄存器(GPIOx_BRR)和一个32位锁定寄存器(GPIOx_LCKR)。
  根据数据手册中列出的每个I/O端口的特定硬件特征, GPIO端口的每个位可以由软件分别配置成多种模式。
  ─ 输入浮空
  ─ 输入上拉
  ─ 输入下拉
  ─ 模拟输入
  ─ 开漏输出
  ─ 推挽式输出
  ─ 推挽式复用功能
  ─ 开漏复用功能
  ----------------------------------------------------
  当然,数据手册上关于IO端口的描述是很多很多的,我也只是大概地了解了一下,真正要设计产品时,肯定还要细看。但至少,知道了IO端口复位后处于浮空状态,也就是其电平状态由外围电路决定,这很重要,如果设计工业品的话,这是必须要确定的;知道了IO引脚可以兼容5V电源;知道了在什么地方可以找到这些引脚在库中的定义而不必看着数据手册去控制那些位;也知道了这些引脚的一些基本操作函数(连猜带蒙带测试应该可以搞定大部分功能),那么我心里基本就有底啦。

上一页  [1] [2] [3] [4]  下一页


本文关键字:单片机  程序  电脑-单片机-自动控制电子学习 - 基础知识 - 电脑-单片机-自动控制