STM32F103 Proteus 仿真 编译用GCC

原理图只要一个MCU就可以了,
双击MCU,编辑固件,选择GCC for ARM,由模板创建默认工程。
会遇到两个错误:
    1.STM32 GCC ARM 编译 _STATIC_INLINE 出错,
         在第一个出错的位置钱加上 #define __STATIC_INLINE static inline 定义成小写
    2.仿真缺少模拟电源网配置错误
          在原理图菜单: 设计/电源线配置中 把未连接的网络标号加进右边对应的网络中即可。

基本环境做好后,就可以开始搞了:
1.看一下编译过程,就是工程中的5个C文件,没有汇编。
2. 进入仿真看一下启动,停在中断文件的复位句柄处,再结合ld文件看一下程序入口,
文件vtable.c 定义的函数指针表,在.vectors,在ld文件中是在FLASH内存中,从0x08000000开始。
启动入口就是vtable.c的中断函数表,复位Reset_Handler(void)中调用main(),没有初始化过程。
3.改写main.c进行编程仿真调试。在网上找一些合适的资料做为参考,选用了STM32不完全手册-寄存器篇作为参考。
如果是工作中用,用库函数更方便维护和移植,对业余爱好和初学者,我觉得用寄存器最合适。
4.首先是初始化系统时钟,不过有个问题,PLL倍频似乎不起作用,最终的系统时钟还是在原理图的MCU晶振频率中设置的6M.
5.配置GPIO,配合虚拟示波器,观察IO输出波形,输入要外接上拉电阻,配置的内部上拉没起作用。
6.编写串口,用虚拟终端观察输出。开始不清楚系统时钟,可用数字示波器根据TXD波形和外接激励源波形推算系统时钟。
7.串口增加中断接收。
8.配置通用定时中断。

 

原理图:

 

 

测试程序:

/* Main.c file generated by New Project wizard
 *
 * Created:   2023-1-6
 * Processor: STM32F103T6
 * Compiler:  GCC for ARM
 */

#include <stm32f1xx.h>

#define u8 unsigned char 
#define u16 unsigned short
#define u32 unsigned int

void delay(int k)
{
int i;
for(i = 0; i<k; i++);
}


//初始化系统时钟,PLL工作不正常(修改PLL倍频没效果),
void clk_init(void)
{

RCC->CR&=~(1<<24);    //关闭PLL后才能配置PLL

//用8M晶振作为PLL时钟源,9倍频72M PLLCLK, AHB不分频72M HCLK系统时钟, APB1 2分频 32M低速外设, APB2 不分频 72M高速外设
RCC->CR|=(1<<16); //外部高速时钟使能 HSEON 。  //复位值是83,用内部时钟

while(!(RCC->CR>>17)& 0x1);//等待外部时钟就绪

RCC->CFGR |= (0b100<<8); //APB2[13:11]=DIV1  000不分频;  APB1[10:8]=DIV2   100 :2分频; AHB[7:4]=DIV1 000不分频;   //复位值是0
RCC->CFGR|=(0b0111<<18);  //设置 PLL[21:18] 值 2~16  9倍频
RCC->CFGR|=1<<16; //  PLLSRC: HSE 作为 PLL的时钟源
//RCC->CFGR &=~(1<<16); //  PLLSRC: HSI/2 作为 PLL的时钟源

//FLASH->ACR|=0x32; //FLASH 2 个延时周期
RCC->CR|=(1<<24); //PLLON[24]  打开PLL
while(!(RCC->CR>>25));//等待 PLL 锁定
RCC->CFGR|=0x00000002;//SW[1:0]  10 PLL 作为系统时钟

while((RCC->CFGR & 0x0000000C) !=0x00000008); // 等待系统时钟状态为 PLL。  SWS[3:2]  10 PLL作为系统时钟设置成功
}

//SCB->AIRCR 优先级寄存器,AIRCR[10:8] 配置分组 ,由分组确定NVIC->IP[240][7:4]  的5种方式的优先级配置 
//分组在设置时,没有涉及中断号,应该是全局的,在系统初始化时配置比较合适,单个中断不需要配置分组
int XGZ_NVIC_PriorityGroupConfig(u8 NVIC_Group)
{
u32 temp;
temp = SCB->AIRCR;//读取先前的设置
temp&=0X0000F8FF; //清空先前分组

switch(NVIC_Group)
{
case 0:
    temp|= (0b111<<8); 
break;
case 1:
    temp|= (0b110<<8); 
break;
case 2:
    temp|= (0b101<<8); 
break;
case 3:
    temp|= (0b100<<8); 
break;
case 4:
    temp|= (0b011<<8); 
break;
default: 
     return -1;  //组号不能大于4
break;
}

temp|=0X05FA0000; //写入钥匙
SCB->AIRCR=temp; //设置分组
return 1;
}

//抢占优先级,响应优先级, 中断号,      
void XGZ_NVIC_Init(u8 NVIC_PreemptionPriority,u8 NVIC_SubPriority,u8 NVIC_Channel)
{
u32 temp;

temp = SCB->AIRCR;//读取先前的设置
temp&=0X00000700; //读取先前分组
NVIC->IP[NVIC_Channel]&=0x0F; //高4位清0
switch(temp)
{
case 0X00000700: //0
    NVIC->IP[NVIC_Channel]|= (NVIC_SubPriority&0xf)<<4;
break;
case 0X00000600: //1
    NVIC->IP[NVIC_Channel]|= (NVIC_PreemptionPriority&0x1)<<7;
    NVIC->IP[NVIC_Channel]|= (NVIC_SubPriority&0x7)<<4;
break;
case 0X00000500: //2
    NVIC->IP[NVIC_Channel]|= (NVIC_PreemptionPriority&0x3)<<6;
    NVIC->IP[NVIC_Channel]|= (NVIC_SubPriority&0x3)<<4;
break;
case 0X00000400: //3
    NVIC->IP[NVIC_Channel]|= (NVIC_PreemptionPriority&0x7)<<5;
    NVIC->IP[NVIC_Channel]|= (NVIC_SubPriority&0x1)<<4;
break;
case 0X00000300: //4
    NVIC->IP[NVIC_Channel]|= (NVIC_PreemptionPriority&0xF)<<4;
break;
default: 
     return;  //会不会没有分组号
break;

}

NVIC->ISER[NVIC_Channel/32]|=(1<<NVIC_Channel%32);  //中断使能,ISER[8] 每一位是一个中断,103的头文件中42个中断
//NVIC->ICER[NVIC_Channel/32]|=(1<<NVIC_Channel%32);  //中断清除使能,因为寄存器是写1有效
//NVIC->ISPR[NVIC_Channel/32]|=(1<<NVIC_Channel%32);  //中断挂起,
//NVIC->ICPR[NVIC_Channel/32]|=(1<<NVIC_Channel%32);  //中断清除挂起,
//NVIC->IABR[NVIC_Channel/32] == 1;//中断激活标志,只读,表示中断正在执行

}

void USART1_IRQHandler(void)
{
    unsigned char res;
    
    res=USART1->DR;

USART1->DR=res;
    while((USART1->SR&0X40)==0);//等待发送结束
        
}

//初始化串口
void uart_init(int bound)
{

int pclk2 = 6000000;   //怎么配置都是HSI

float temp;
unsigned short mantissa;
unsigned short  fraction;
temp=(float) pclk2 / (bound*16);//得到 USARTDIV
mantissa=temp; //得到整数部分
fraction=(temp-mantissa)*16; //得到小数部分
mantissa<<=4;
mantissa+=fraction;

RCC->APB2ENR|=1<<2; //使能 PORTA 口时钟
RCC->APB2ENR|=1<<14; //使能串口时钟
GPIOA->CRH&=0XFFFFF00F; //IO 状态设置
GPIOA->CRH|=0X000008B0; //IO 状态设置
RCC->APB2RSTR|=1<<14; //复位串口 1
RCC->APB2RSTR&=~(1<<14);//停止复位
//波特率设置
USART1->BRR=mantissa; // 波特率设置
USART1->CR1|=0X200C; //1 位停止,无校验位.

USART1->CR1|=1<<8; //PE 中断使能
USART1->CR1|=1<<5; //接收缓冲区非空中断使能

XGZ_NVIC_Init(3,3,USART1_IRQn);// 抢占3,响应3,

}

void send(char c)
{
    USART1->DR=c;
    while((USART1->SR&0X40)==0);//等待发送结束
   
}

void sendstring(char* s)
{
    while(*s)
{
  send(*s++);
}
   
}

int t3cnt=0;
void TIM3_IRQHandler(void)
{
if(TIM3->SR&0X0001) 
{
//send('T');
t3cnt++;
if(t3cnt>1)
{
GPIOA->ODR|=1<<8;
t3cnt = 0;
}
else
{
GPIOA->ODR&=~(1<<8);
}


}
TIM3->SR&=~(1<<0);//清除中断标志位
}


void TIM3_Int_Init(u16 arr,u16 psc)
{
RCC->APB1ENR|=1<<1; //TIM3 时钟使能
TIM3->ARR=arr; //设定计数器自动重装值
TIM3->PSC=psc; //预分频器 
TIM3->DIER|=0x01; //允许更新中断

//TIM3->CR1&=~((1<<4)); //向上和下计数,应该都是0 和 装载值之差

TIM3->CR1|=(1<<7)|(1<<0); //自动装载?, 使能定时器 3,

XGZ_NVIC_Init(1,3,TIM3_IRQn);//抢占 1,子优先级 3,
}

void gpio_init(void)
{

RCC->APB2ENR |= (0b01<<2);   
RCC->APB2ENR |= (0b01<<4); 


//PP50M 输出  0011; 上拉输入1000
GPIOA->CRL &= 0xFFFF00FF;  //PB2 4 清除原配置
GPIOA->CRL |=  0x00003800;   //PB2 上拉输入    PB3 PP50M 出
GPIOA->ODR|=1<<2;

GPIOA->CRH &= 0xFFFFFFF0;
GPIOA->CRH |= 0x00000003;
}


int main (void)
 { 
  
//初始化系统时钟,则上电默认HSI作为系统时钟
//SystemInit ();

clk_init();  //PLL 不正常,仿真不必配置

XGZ_NVIC_PriorityGroupConfig(2); //为系统配置中断分组2

gpio_init();

uart_init(9600);
sendstring("This is a test 123\r\n");

TIM3_Int_Init(1000, 6-1);  // 1k:  6M/2 x2 APB1 2分频 , TMR时钟源是分频APB的2倍,ABP1不分频的话直接用

char data = 0;
  while (1)
{

if(!(GPIOA->IDR&0x04))  //好像必须外部上拉
{
send('K');
}

delay(10000);
}
      
   return 0;
 }   

 

运行效果

 

 

调试界面:

 

posted @ 2023-01-06 11:29  XGZ21  阅读(701)  评论(0编辑  收藏  举报