家电维修班,手机维修班,电脑维修班,电工班,焊工班,液晶电视维修班,电动工具维修班、电动车摩托车维修班、网络营销培训、网站设计培训、淘宝培训---全国招生 家电维修班,手机维修班,电脑维修班,电工班,焊工班,液晶电视维修班,电动工具维修班、电动车摩托车维修班、网络营销培训、网站设计培训、淘宝培训---全国招生
您的位置:网站首页 > 电器维修资料网 > 正文 >

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

★★★★★【文章导读】:STM32单片机基础学习:从勉强看懂一行程序到IO口研究具体内容是:刚好勉勉强强看懂一行程序继续学习中,先把开发板自带一个例子做了些精简,以免看得吓人。。。。就是这个,让portd上接的4个led分别点亮。开始研究代码intmain(void){init_all_periph();。。.。。.看到这一行,开…

来源: 日期:2013-11-29 9:19:06 人气:标签:

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

     刚好勉勉强强看懂一行程序

  继续学习中,先把开发板自带一个例子做了些精简,以免看得吓人。。。。
  
  就是这个,让portd上接的4个led分别点亮。
  开始研究代码
  int main(void)
  {
  init_all_periph();
  。。.。。.
  看到这一行,开始跟踪,于是又看到了下面的内容
  void init_all_periph(void)
  {
  rcc_configuration();
  。。.。。.
  继续跟踪
  void rcc_configuration(void)
  {
  systeminit();
  。。.。。.
  这行代码在system_stm32f10x.c中找到了。
  void systeminit (void)
  {
  /* reset the rcc clock configuration to the default reset state(for debug purpose) */
  /* set hsion bit */
  rcc-》cr |= (uint32_t)0x00000001;
  /* reset sw, hpre, ppre1, ppre2, adcpre and mco bits */
  #ifndef stm32f10x_cl
  rcc-》cfgr &= (uint32_t)0xf8ff0000;
  #else
  rcc-》cfgr &= (uint32_t)0xf0ff0000;
  #endif /* stm32f10x_cl */
  /* reset hseon, csson and pllon bits */
  rcc-》cr &= (uint32_t)0xfef6ffff;
  /* reset hsebyp bit */
  rcc-》cr &= (uint32_t)0xfffbffff;
  /* reset pllsrc, pllxtpre, pllmul and usbpre/otgfspre bits */
  rcc-》cfgr &= (uint32_t)0xff80ffff;
  #ifndef stm32f10x_cl
  /* disable all interrupts and clear pending bits */
  rcc-》cir = 0x009f0000;
  #else
  /* reset pll2on and pll3on bits */
  rcc-》cr &= (uint32_t)0xebffffff;
  /* disable all interrupts and clear pending bits */
  rcc-》cir = 0x00ff0000;
  /* reset cfgr2 register */
  rcc-》cfgr2 = 0x00000000;
  #endif /* stm32f10x_cl */
  /* configure the system clock frequency, hclk, pclk2 and pclk1 prescalers */
  /* configure the flash latency cycles and enable prefetch buffer */
  setsysclock();
  }
  这一长串的又是什么,如何来用呢?看来,偷懒是不成的了,只能回过头去研究stm32的时钟构成了。
  相当的复杂。

     系统的时钟可以有3个来源:内部时钟hsi,外部时钟hse,或者pll(锁相环模块)的输出。它们由rcc_cfgr寄存器中的sw来选择。
  sw(1:0):系统时钟切换
  由软件置’1’或清’0’来选择系统时钟源。 在从停止或待机模式中返回时或直接或间接作为系统时钟的hse出现故障时,由硬件强制选择hsi作为系统时钟(如果时钟安全系统已经启动)
  00:hsi作为系统时钟;
  01:hse作为系统时钟;
  10:pll输出作为系统时钟;
  11:不可用。
  ////////////////////////////////////////////////////////////////////
  pll的输出直接送到usb模块,经过适当的分频后得到48m的频率供usb模块使用。
  系统时钟的一路被直接送到i2s模块;另一路经过ahb分频后送出,送往各个系统,其中直接送往sdi,fmsc,ahb总线;8分频后作为系统定时器时钟;经过apb1分频分别控制plk1、定时器tim2~tim7;经过apb2分频分别控制plk2、定时器tim1~tim8、再经分频控制adc;
  由此可知,stm32f10x芯片的时钟比之于51、avr、pic等8位机要复杂复多,因此,我们立足于对着芯片手册来解读程序,力求知道这些程序代码如何使用,为何这么样使用,如果自己要改,可以修改哪些部分,以便自己使用时可以得心应手。
  单步执行,看一看哪些代码被执行了。
  /* reset the rcc clock configuration to the default reset state(for debug purpose) */
  /* set hsion bit */
  rcc-》cr |= (uint32_t)0x00000001;
  120s52109-1.jpg
  这是rcc_cr寄存器,由图可见,hsion是其bit 0位。
  hsion:内部高速时钟使能
  由软件置’1’或清零。
  当从待机和停止模式返回或用作系统时钟的外部4-25mhz时钟发生故障时,该位由硬件置’1’来启动内部8mhz的rc振荡器。当内部8mhz时钟被直接或间接地用作或被选择将要作为系统时钟时,该位不能被清零。
  0:内部8mhz时钟关闭;
  1:内部8mhz时钟开启。
  ///////////////////////////////////////////////////////////////////////
  /* reset sw, hpre, ppre1, ppre2, adcpre and mco bits */
  #ifndef stm32f10x_cl
  rcc-》cfgr &= (uint32_t)0xf8ff0000;
  点击看大图
  这是rcc_cfgr寄存器
  该行程序清零了mc0[2:0]这三位,和adcpre[1:0],ppre2[2:0],ppre1[2:0],hpre[3:0],sws[1:0]和sw[1:0]这16位。
  /*
  mco: 微控制器时钟输出,由软件置’1’或清零。
  0xx:没有时钟输出;
  100:系统时钟(sysclk)输出;
  101:内部8mhz的rc振荡器时钟输出;
  110:外部4-25mhz振荡器时钟输出;
  111:pll时钟2分频后输出。
  */
  /* reset hseon, csson and pllon bits */
  rcc-》cr &= (uint32_t)0xfef6ffff;
  清零了pllon,hsebyp,hserdy这3位。
  /* reset hsebyp bit */
  rcc-》cr &= (uint32_t)0xfffbffff;
  清零了hsebyp位 ///???为什么不一次写??
  hsebyp:外部高速时钟旁路,在调试模式下由软件置’1’或清零来旁路外部晶体振荡器。只有在外部4-25mhz振荡器关闭的情况下,才能写入该位。
  0:外部4-25mhz振荡器没有旁路;
  1:外部4-25mhz外部晶体振荡器被旁路。
  所以要先清hseon位,再清该位。
  /* reset pllsrc, pllxtpre, pllmul and usbpre/otgfspre bits */
  rcc-》cfgr &= (uint32_t)0xff80ffff;
  清零了:usbpre,pllmul,pllxtpr,pllsrc共7位
  /* disable all interrupts and clear pending bits */
  rcc-》cir = 0x009f0000;
  ////这个暂不解读
  setsysclock();
 跟踪进入该函数,可见一连串的条件编译:

 
  单步运行,执行的是:
  #elif defined sysclk_freq_72mhz
  setsysclockto72();
  为何执行该行呢,找到sysclk_preq_**的相关定义,如下图所示。
  
  这样就得到了我们所要的一个结论:如果要更改系统工作频率,只需要在这里更改就可以了。
  可以继续跟踪进入这个函数来观察如何将工作频率设定为72mhz的。
  static void setsysclockto72(void)
  {
  __io uint32_t startupcounter = 0, hsestatus = 0;
  /* sysclk, hclk, pclk2 and pclk1 configuration ---------------------------*/
  /* enable hse */
  rcc-》cr |= ((uint32_t)rcc_cr_hseon);
  //开启hse
  /* wait till hse is ready and if time out is reached exit */
  do
  {
  hsestatus = rcc-》cr & rcc_cr_hserdy;
  startupcounter++;
  } while((hsestatus == 0) && (startupcounter != hsestartup_timeout));
  //等待hse确实可用,这有个标志,即rcc_cr寄存器中的hserdy位(bit 17),这个等待不会无限长,有个超时策略,即每循环一次计数器加1,如果计数的次数超过hsestartup_timeout,就退出循环,而这个hsestartup_timeout在stm32f10x.h中定义,
  #define hsestartup_timeout ((uint16_t)0x0500) /*!《 time out for hse start up */
  ///////////////////////////////////////////////////////////////////////////////////////////////
  if ((rcc-》cr & rcc_cr_hserdy) != reset)
  {
  hsestatus = (uint32_t)0x01;
  }
  else
  {
  hsestatus = (uint32_t)0x00;
  }
  ///再次判断hserdy标志位,并据此给hsestatus变量赋值。
  if (hsestatus == (uint32_t)0x01)
  {
  /* 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的好处了,这块熟啊,几乎没有花时间在上面,一用就成了。

  • 1
  • 2
  • 下一页

【看看这篇文章在百度的收录情况】

联系方式

  • 0731-85579057 , 0731-85569651
  • 点击这里给我发消息点击这里给我发消息点击这里给我发消息
网站栏目导航: 培训课程 手机硬件 手机软件 综合维修 学校资讯 考证指南 就业导航 招生指南 教学管理 入学须知 学校图片 教学大纲 师资力量 学生感言 学校概况 教学实景 手机维修培训资讯 电脑维修培训 维修间故事 手机维修培训 液晶电视维修培训 家电维修资料网 电器维修资料网 招生地区 刷机教程 家电维修 手机技巧 老版网站 招生平台网络工程
友情链接: 监控安装培训 电动工具维修 家电维修学校 电工培训学校 液晶电视维修 焊工培训学校 电工焊工学校 电脑维修学校 家电维修培训 电脑维修培训 家装电工培训网络安装维护 主板维修 液晶显示器 笔记本电脑维修 电脑组装维护 电脑硬件维修 电脑维修 电工考证 电工证 装修电工 水电工 维修电工 电工 焊接技术 电焊工 焊工 电动设备维修 电动工具维修 制冷维修 空调维修 冰箱维修  更多>>
阳光-手机维修教育品牌学校
点击这里给我发消息 点击这里给我发消息 点击这里给我发消息
电工培训学校 电动车维修学校 摩托车维修学校 摩托车维修培训 手机维修培训 家电维修培训 电脑维修培训 电动工具维修培训 液晶电视维修培训 安防监控培训 空调维修培训 网络营销培训 网站设计培训 淘宝网店培训 电器维修培训 家电维修学校 电工培训 焊工培训 电工学校 电工培训学校 电动车维修学校 摩托车维修学校 摩托车维修培训 手机维修培训 家电维修培训 电脑维修培训 电动工具维修培训 液晶电视维修培训 安防监控培训 空调维修培训 网络营销培训 网站设计培训 淘宝网店培训 电器维修培训 家电维修学校 电工培训 焊工培训 电工学校 电工培训学校 电动车维修学校 摩托车维修学校 摩托车维修培训 手机维修培训 家电维修培训 电脑维修培训 电动工具维修培训 液晶电视维修培训 安防监控培训 空调维修培训 网络营销培训 网站设计培训 淘宝网店培训 电器维修培训 家电维修学校 电工培训 焊工培训 电工学校 电工培训学校 电动车维修学校 摩托车维修学校 摩托车维修培训 手机维修培训 家电维修培训 电脑维修培训 电动工具维修培训 液晶电视维修培训 安防监控培训 空调维修培训 网络营销培训 网站设计培训 淘宝网店培训 电器维修培训 家电维修学校 电工培训 焊工培训 电工学校
中山市,固原市,银川市,玉树,海东,陇南市,酒泉市,张掖市,天水市,金昌市,兰州市,榆林市,延安市,渭南市,铜川市,阿里,山南,拉萨市,怒江,文山州,楚雄州,普洱市,昭通市,玉溪市,昆明市,毕节,铜仁,遵义市,贵阳市,甘孜州,资阳市,达州市,宜宾市,南充市,遂宁市,绵阳市,泸州市,自贡市,三亚市,崇左市,河池市,玉林市,钦州市,梧州市,柳州市,梅州市,肇庆市,湛江市,佛山市,珠海市,韶关市,湘西州,怀化市,郴州市,张家界市,邵阳市,株洲市,仙桃市,随州市,荆州市,荆门市,襄樊市,黄石市,驻马店市,信阳市,南阳市,漯河市,中卫市,石嘴山市,海西,海南藏州,黄南州,海北,甘南,庆阳市,平凉市,武威市,白银市,嘉峪关市,安康市,汉中市,咸阳市,宝鸡市,林芝,日喀则,昌都,迪庆,德宏,大理,西双版纳,红河州,临沧市,丽江市,保山市,曲靖市,黔东州,黔西州,安顺市,六盘水市,凉山州,阿坝州,雅安市,广安市,眉山市,内江市,广元市,德阳市,攀枝花市,成都市,海口市,来宾市,百色市,贵港市,北海市,桂林市,南宁市,云浮市,揭阳市,潮州市,清远市,阳江市,汕尾市,惠州市,茂名市,江门市,汕头市,深圳市,广州市,娄底市,永州市,益阳市,岳阳市,湘潭市,长沙市,恩施州,黄冈市,孝感市,鄂州市,十堰市,武汉市,周口市,商丘市,三门峡市,许昌市,焦作市,安阳市,鹤壁市,平顶山市,开封市,郑州市,聊城市,滨州市,德州市,莱芜市,日照市,泰安市,烟台市,潍坊市,东营市,淄博市,上饶市,济南市,抚州市,宜春市,赣州市,新余市,九江市,景德镇市,宁德市,南平市,泉州市,莆田市,厦门市,宣城市,亳州市,六安市,宿州市,黄山市,滁州市,安庆市,淮北市,马鞍山市,蚌埠市,芜湖市,合肥市,丽水市,舟山市,衢州市,金华市,湖州市,嘉兴市,宁波市,宿迁市,镇江市,盐城市,连云港市,苏州市,徐州市,南京市,绥化市,牡丹江市,佳木斯市,大庆市,鹤岗市,哈尔滨市,白城市,白山市,辽源市,吉林市,葫芦岛市,铁岭市,盘锦市,阜新市,锦州市,本溪市,鞍山市,沈阳市,锡林郭勒盟,通辽市,乌海市,吕梁市,忻州市,晋中市,晋城市,阳泉市,太原市,廊坊市,承德市,保定市,邯郸市,唐山市,宁夏,甘肃省,西藏,贵州省,重庆市,广西,湖南省,河南省,江西省,安徽省,江苏省,黑龙江省,辽宁省,山西省,天津市,四平市,内蒙古,吴忠市,果洛,西宁市,定西市,商洛市,西安市,那曲,黔南州,巴中市,乐山市,贺州市,防城港市,东莞市,河源市,常德市,衡阳市,咸宁市,宜昌市,濮阳市,新乡市,洛阳市,菏泽市,临沂市,威海市,济宁市,枣庄市,青岛市,吉安市,鹰潭市,萍乡市,南昌市,龙岩市,漳州市,三明市,福州市,池州市,巢湖市,阜阳市,铜陵市,淮南市,台州市,绍兴市,温州市,杭州市,泰州市,扬州市,淮安市,南通市,常州市,无锡市,大兴安岭,黑河市,七台河市,伊春市,双鸭山市,鸡西市,齐齐哈尔市,延边,松原市,通化市,长春市,朝阳市,辽阳市,营口市,丹东市,抚顺市,大连市,阿拉善盟,兴安盟,乌兰察布市,巴彦淖尔市,呼伦贝尔市,鄂尔多斯市,赤峰市,包头市,呼和浩特市,临汾市,运城市,朔州市,长治市,大同市,衡水市,沧州市,张家口市,邢台市,秦皇岛市,石家庄市,青海省,陕西省,云南省,四川省,海南省,广东省,湖北省,山东省,福建省,浙江省,上海市,吉林省,河北省,北京市