原理图只要一个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;
}
运行效果
调试界面: