Proteus内部编程仿真STM32:实现轻松的代码调试

目录

   一、背景

                     二、STM32开发简介

                     三、STM32实例简述

                     四、Proteus内部寄存器编程

                        1、时钟函数Clock_Init()

                        2、GPIO函数 LED_Init()

                        3、延时函数 delay_nms()

                        4、主函数main()

                        5、宏定义

                      五、仿真结果图示

                ​​​​​​​      六、完整代码

                ​​​​​​​      七、总结


一、背景

想在没有开发板的基础上形象逼真仿真基于STM32的设计,除了Proteus无出之右了吧,但目前没有很好地指导直接在Proteus中C编程来仿真STM32设计的帖子供参考,绝大部分的帖子还是借助于Keil MDK或者STM32CubeMX之类的工具,编译成HEX文件之后导入Proteus仿真,程序需要修改时,就得来回切换,反复修改程序、编译程序、导入HEX文件,调试起来非常不方便,也没有充分利用Proteus的优势,费时费力,降低调试效率。

二、STM32开发简介

STM32开发可以分别基于HAL库、库函数或寄存器编程来进行,而基于Proteus链接的Keil for ARM编译器生成的代码(如图1),相比HAL和库函数不具有优势,但HAL(硬件抽象层)和库函数也无非是寄存器软件化,使软件编程更方便,可读性更强,但基于寄存器编程也有代码简略,可以清晰了解编程意图的优点,下面我们以寄存器编程为主导举一个例子来介绍一下在Proteus内部C语言编程仿真STM32设计的实际应用。

 图1 Proteus Keil for ARM编译器生成的代码

三、STM32实例简述

选用STM32F103T6芯片,利用PA8/PB2的推挽输出以1秒为周期交替点亮熄灭白色LED灯,系统时钟采用PLL锁相环9倍频8MHz的HSE外部晶振源来获得,电路原理如下图2,Keil for ARM编译器的设置,可以参考Keil for C51的指导设置,详情请参看下面我之前的帖子,链接如下:

Proteus和Keil C51联调仿真完整解析(附程序)_宝玉飞的博客-CSDN博客

图2  STM32F103T6电路原理图

四、Proteus内部寄存器C语言编程

1、时钟函数Clock_Init()

熟悉一个芯片先从时钟开始,这话一点都不假,我们的设置也是先从时钟入手,分别使能内外时钟并等待就绪,APB1二分频,APB2和AHB不分频,PLL对外部时钟HSE先9倍频再赋给系统时钟,代码如图3:

 图3 时钟设置

2、GPIO函数 LED_Init()

先使能GPIO时钟,初始化PA8/PB2之后,设置为推挽逻辑高初始输出。代码如图4:

 图4 GPIO设置

3、延时函数 delay_nms()

外部晶振源是8MHz,9倍频后就是72MHz,延时0.5秒的话,需要delay_nms(250)。代码如图5:

图5 延时函数 

4、主函数main()

调用Clock时钟和GPIO初始化函数,然后以1秒为周期循环点亮和熄灭LED灯。代码如图6:

 图6 主函数

5、宏定义

紧跟头文件定义了GPIO操作的宏定义,位带操作可以像C51对GPIO进行位操作。代码如图7:

图7 GPIO宏定义

 五、仿真结果图示

图8的仿真结果借助了GIF图,实际亮灭切换没这么快。

 图8 仿真结果

六、完整代码

几乎每行代码都给出了注释,方便快速理解。

/* Main.c file generated by liyufei
 *
 * Created:   周六 4月 9 2022
 * Processor: STM32F103T6
 * Compiler:  Keil for ARM
 */

#include <stm32f103x6.h>
#include <stm32f1xx.h> 

//IO口操作宏定义,位带操作,实现51类似的GPIO控制功能
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 

#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C 
#define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C 
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //PA输出 
#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //PB输出 

#define LED0 PAout(8) // PA8 
#define LED1 PBout(2) // PB2

//时钟设置
//RCC_CR_HSION,RCC_CR_HSEON,RCC_CR_HSERDY,RCC_CFGR_PLLSRC等在头文件stm32f103x6.h有定义
void Clock_Init(unsigned char PLL) 
   { 
      unsigned char temp_value = 0;
      
      RCC->CR|=RCC_CR_HSION;    //使能内部高速时钟HSION
      while(!(RCC_CR_HSIRDY>>1));  //等待PLL锁定
      RCC->CR|=RCC_CR_HSEON;    //外部高速时钟使能HSEON 
      while(!(RCC_CR_HSERDY>>17)); //等待外部时钟就绪
      RCC->CFGR=0x00000400;      //APB1=DIV2;APB2=DIV1;AHB=DIV1;
      RCC->CFGR|=(PLL-2)<<18;      //设置 PLL值 2~16,本例为9倍频 
      RCC->CFGR|=RCC_CFGR_PLLSRC;  //PLLSRC选择外部时钟HSE为PLL输入源 
      RCC->CR|=RCC_CR_PLLON;       //使能PLL 
      while(!(RCC_CR_PLLRDY>>25)); //等待 PLL锁定
      RCC->CFGR|=RCC_CFGR_SW_PLL; //PLL作为系统时钟      
      while(temp_value!=RCC_CFGR_SWS_PLL) //等待PLL作为系统时钟设置成功
      { 
	 temp_value=RCC->CFGR;
	 temp_value&=0x0C;
      }       
   }

//毫秒延时
void delay_nms(unsigned int time)
{    
   unsigned int i=0;  
   while(time--)
   {
      i=12000;  // 250x12000 = 36000000,每隔0.5秒LED灯反转
      while(i--) ;    
   }
}

//为LED亮灭进行GPIO口初始化
void LED_Init(void)
 { 
     
   RCC->APB2ENR|= RCC_APB2ENR_IOPAEN; //使能 PORTA时钟
   RCC->APB2ENR|= RCC_APB2ENR_IOPBEN; //使能 PORTB时钟
   GPIOA->CRH&=0XFFFFFFF0;  //初始化PA8
   GPIOA->CRH|=0X00000003;  //PA8 推挽输出
   GPIOA->ODR|=1<<8;        //PA8 初始输出高
   GPIOB->CRL&=0XFFFFF0FF;  //初始化PB2
   GPIOB->CRL|=0X00000300;  //PB2 推挽输出
   GPIOB->ODR|=1<<2;        //PB2 初始输出高
   }

//main函数
int main (void)
 {    
   Clock_Init(9); //设PLL为系统时钟,频率为HSE外部时钟8MHz的9倍   
   LED_Init(); //GPIO初始化   
   while (1)
   {
      LED0 = 0; //PA8输出0
      LED1 = 1; //PB2输出1
      delay_nms(250); //延时0.5S
      LED0 = 1; //PA8输出1
      LED1 = 0; //PB2输出0
      delay_nms(250); //延时0.5S
   }
}   

七、总结

上面例子的仿真成功,至少说明利用Proteus内部C语言编程进行STM32的设计仿真是行得通的,STM32的仿真有别路可选。

物联沃分享整理
物联沃-IOTWORD物联网 » Proteus内部编程仿真STM32:实现轻松的代码调试

发表评论