首页 > 其他分享 >STM32F401 Proteus 仿真 串口两种发送方式 编译用GCC ,寄存器配置方式

STM32F401 Proteus 仿真 串口两种发送方式 编译用GCC ,寄存器配置方式

时间:2023-01-24 09:11:15浏览次数:49  
标签:GCC 1CR1 tx NVIC Proteus 串口 usart2 Channel

用的proteus 8.9 中文版,STM32F401可能是支持的最复杂的MCU了吧,就用这个做实验了。

编译器用GCC,在proteus中安装调试都很方便,编程实验用寄存器配置方式,因为仅仅是学习,简单直接,方便调试。

先在网上找一些中文资料,401的中文资料感觉不如103的多,就结合103的一起看。

在前几天学的STM32F103的基础上改改即可。

还是主要学一下串口,因为串口是最常见最通用的接口,并且涉及其他常用的时钟,中断等。

测试工程的建立过程和103一样:

先画个原理图,用虚拟示波器配合激励信号调试时钟,用虚拟中断调试串口。

双击MUC编辑固件,选GCC创建软件框架,和103有点不同,入口是一个汇编程序,而之前103的模板都是C

调试还是从时钟,GPIO开始,401的PLL工作正常,之前103的是不正常的。

接下来是中断,定时器,串口。

proteus中支持的这款401的管脚比较多,做实验很方便。

串口1配置用TC发送,TC是发送完中断,软件清标志,这里用查询TC标志的方式发送,不用缓冲区,简单直接

串口2配置用TXE发送,TXE是TDR空中断,这个只要空就中断,而不是变空才触发中断,也就是空了后要及时关闭中断,否则一直会中断。

原理图如下:

 

测试程序就一个main.c其他的proteus自动生成,就看看,不用改。

串口1 查询TC标志发送

串口2 用TXE中断,环形缓冲队列发送

如果用TC中断,缓冲区发,则第一个发送需要软件触发

/* Main.c file generated by New Project wizard
 *
 * Created:   2023-1-24
 * Processor: STM32F401VE
 * Compiler:  GCC for ARM
    配置时钟
    配置  GPIOD    
    配置通用定时器
   配置中断串口:USART1 单个字节查询TC状态发送。如果用中断缓冲队列,需要第一个发送触发,
                         USART2 用TXE中断,TXEIE开关控制,环形队列缓冲发送,
 */

#include <stm32f4xx.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++);
}


//
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 clk4_init(void)
{

RCC->CR|=RCC_CR_HSION;

//配置时钟 
RCC->CR&=~(1<<24);    //关闭主PLL
// PLLQ 外设48M分频系数:0111 7分频率;PLLSRC :1 HSE 作为源; PLLP:PLLCLK分频系数 00  2分频; PLLN:倍频系数 192-432;PLLM  VCCO主分频系数8
// [HSI] -PLLM -PLLN -PLLP -[PLLCLK]-        [8 ] / 8 X 336 / 2 =168
RCC->PLLCFGR=(7<<24)|(1<<22)|(0<<16)|(336<<6)|(8<<0);
RCC->CR|=1<<24;            //打开主PLL

RCC->CFGR|=(4<<13)|(5<<10)|(0<<4);//PPRE2  100 :APB2 2分频 ; PPRE1 101:APB1 4分频;    HPRE:0XXX: HCLK 不分频; HCLK =168M = FCLK

RCC->CFGR|=2<<0;           //选择主PLL作为系统时钟      168M
//RCC->CFGR|=1<<0;        //选择主HSE作为系统时钟     
//RCC->CFGR|=0<<0;        //选择主HSi作为系统时钟    

}

//4个寄存器配置

void gpio4_init(void)
{
     
     RCC->AHB1ENR |=0b11111;   //GPIO  ABCDE全打开

     GPIOD->MODER =0xFFFF0000;  //  高8位输出,低8位输入
     GPIOD->OTYPER =0;               //输出推挽
     GPIOD->OSPEEDR = 0xAAAAAAAA;  // 10输出50M
     GPIOD->PUPDR = 0x55555555;   // 端口上拉, 00浮空,01上拉,10下拉
     GPIOD->ODR = 0xFFFFFFFF; //输出 1
   
}

void sendstring(u8 chan, char* s);

int t3cnt=0;
int t3cnt10s=0;

void TIM3_IRQHandler(void)
{
if(TIM3->SR&0X0001) 
{

    t3cnt++;
    if(t3cnt>1)
    {
        GPIOD->ODR |= (1<<10);   
        t3cnt = 0;
    }
    else
    {
        GPIOD->ODR&=~(1<<10);
    }

    t3cnt10s++;
    if(t3cnt10s>1000)
    {
        t3cnt10s = 0;
        sendstring(2, "This is TIM3 INT 1000 test.\r\n");
    }

}
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 Usart1_Config(u32 brr)
{
    float div=0;
    u32 div_m=0;//存放整数部分
    u32 div_f=0;//存放小数部分
    //开时钟 PA USART1
    RCC->AHB1ENR |= (1<<0);
    RCC->APB2ENR |= (1<<4);

    //配置工作模式 p9 p10 复用模式做 usart1
    GPIOA->AFR[1] &=~ (0xff<<4);
    GPIOA->AFR[1] |= (0x7<<4);
    GPIOA->AFR[1] |= (0x7<<8);
    //PA9 复用推挽输出
     GPIOA->MODER  &=~ (3<<18);
    GPIOA->MODER |= (2<<18);
    GPIOA->OTYPER &=~ (1<<9);
    GPIOA->OSPEEDR &=~ (3<<18);
    GPIOA->OSPEEDR |= (2<<18);
    GPIOA->PUPDR &=~ (3<<18);
    //pa10 
    GPIOA->MODER &=~ (3<<20);
    GPIOA->MODER |= (2<<20);
    GPIOA->PUPDR &=~ (3<<20);
    //配置串口
    USART1->CR1 &=~ (1<<15);// over8   16位采样
    //USART1->CR1 |= (1<<15);// 8位采样
    USART1->CR1 &=~ (1<<12);//字长1+8+n
    USART1->CR1 &=~ (1<<10);    //禁止奇偶校验
    USART1->CR2 &=~ (3<<12); //停止位
   
    
  //波特率uart2 在AHB2上 时钟2分频
    div = 168000000.0/2/16/brr;   // FCLK /(8 X(2-0) )/ USARTDIV ;  OVER8=0 16倍过采样
    div_m = (u32)div;                //浮点整数位 (12bit)
    div_f = (div-div_m)*16;       //浮点小数位(4bit)
    USART1->BRR = (div_m<<4)|div_f;
   
    USART1->CR1|=1<<2;              //串口接收使能
    USART1->CR1|=1<<3;              //串口发送使能  
    USART1->CR1|=1<<5;                //接收缓冲区非空中断使能    
   // USART1->CR1|=1<<6;                //TCIE   发送完成中断    
   // USART1->CR1|=1<<7;                //TXEIE   USART_SR 变空中断    
    USART1->CR1|=1<<13;              //串口使能  

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

void usart2_init(u32 pclk1,u32 bound)
{       
    
    float div=0;
    u32 div_m=0;//存放整数部分
    u32 div_f=0;//存放小数部分
         
    RCC->AHB1ENR |= (1<<0);
    RCC->APB1ENR |= (1<<17); //uart2 clk

    GPIOA->AFR[0] &=~ (0xff<<8);
    GPIOA->AFR[0] |= (0x7<<8);
    GPIOA->AFR[0] |= (0x7<<12);
    //PA2 复用推挽输出
    GPIOA->MODER  &=~ (3<<4);
    GPIOA->MODER |= (2<<4);
    GPIOA->OTYPER &=~ (1<<2);
    GPIOA->OSPEEDR &=~ (3<<4);
    GPIOA->OSPEEDR |= (2<<4);
    GPIOA->PUPDR &=~ (3<<4);
    //pa3 
    GPIOA->MODER &=~ (3<<6);
    GPIOA->MODER |= (2<<6);
    GPIOA->PUPDR &=~ (3<<4);

    //配置串口
    USART2->CR1 &=~ (1<<15);// over8   16位采样
    //USART2->CR1 |= (1<<15);// 8位采样
    USART2->CR1 &=~ (1<<12);//字长1+8+n
    USART2->CR1 &=~ (1<<10);    //禁止奇偶校验
    USART2->CR2 &=~ (3<<12); //停止位
   
    //波特率 uart2 在AHB1上 时钟4分频
   
    div = (float)(pclk1*1000000)/4/16/bound;  
   // div = 168000000.0/4/16/bound;   // FCLK /(8 X(2-0) )/ USARTDIV ;  OVER8=0 16倍过采样
    div_m = (u32)div;                //浮点整数位 (12bit)
    div_f = (div-div_m)*16;       //浮点小数位(4bit)
    USART2->BRR = (div_m<<4)|div_f;
           
    //波特率设置
     //USART2->BRR=mantissa;             // 波特率设置     
    
    USART2->CR1|=1<<2;              //串口接收使能
    USART2->CR1|=1<<3;              //串口发送使能  
    USART2->CR1|=1<<5;                //接收缓冲区非空中断使能    
    //USART2->CR1|=1<<6;                //TCIE   发送完成中断,没数据时不会中断,平时不用关闭    
   // USART2->CR1|=1<<7;                //TXEIE   USART_TDR 是空中断, 没数据会一直中断,只有发数据时才能打开    
    USART2->CR1|=1<<13;              //串口使能  
    
    //MY_NVIC_Init(0,0,USART3_IRQn,2);//组2,优先级0,0,最高优先级 
     XGZ_NVIC_Init(3,3,USART2_IRQn);// 抢占3,响应3,
    
}


void USART1_IRQHandler(void)
{
    unsigned char res;
   

    if(USART1->SR&(1<<5))  //RXNE
    {
         res=USART1->DR;

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

    if(USART1->SR&(1<<6)) //TC
    {
         USART1->SR&=~(1<<6);   //软件清除TC标志
     }

    if(USART1->SR&(1<<7)) //TXE   
     {
          USART1->CR1&=~(1<<7);   //关闭
     }   
}


int usart2_rx_p1 = 0;
int usart2_rx_p2 = 0;
int usart2_rx_overflow = 0;

int usart2_tx_p1 = 0;
int usart2_tx_p2 = 0;
int usart2_tx_overflow = 0;

#define USART_BUF_RXMAX  64
char usart2_rxbuf[USART_BUF_RXMAX]={0};

#define USART_BUF_TXMAX  64
char usart2_txbuf[USART_BUF_TXMAX]={0};

void USART2_IRQHandler(void)
{
    unsigned char res;

    if(USART2->SR&(1<<5))  //RXNE
    {
         res=USART2->DR;

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

    if(USART2->SR&(1<<6)) //TC
    {
         USART2->SR&=~(1<<6);   //软件清除TC状态标志
     }
    
    if(USART2->SR&(1<<7)) //TXE   
    {

         if(usart2_tx_p2 == usart2_tx_p1)  // 队列空 ,关闭TXEIE 中断
        {
           
              USART2->CR1&=~(1<<7);                //TXEIE   USART_TDR 是空中断, 没数据会一直中断,只有发数据时才能打开    
        }
        else
        {
               USART2->DR=usart2_txbuf[usart2_tx_p1] ;
               usart2_tx_p1++;
               if(usart2_tx_p1 == USART_BUF_TXMAX)
               {
                      usart2_tx_p1 = 0;       
               }
         }
         
     }
       
}

void fputc(char c)
{
    while((USART1->SR & (1<<6)) == 0);
    USART1->DR = c;
   
}


void fputc2(char c)
{
    
    usart2_txbuf[usart2_tx_p2]  = c;
    usart2_tx_p2++;
    
    if(usart2_tx_p2 == USART_BUF_TXMAX)
    {
         usart2_tx_p2 = 0;
    }

    if(usart2_tx_p2 == usart2_tx_p1)
    {
           usart2_tx_overflow = 1;  //overflow
     }

     USART2->CR1|=1<<7;                //TXEIE   USART_TDR 是空中断, 没数据会一直中断,只有发数据时才能打开    
     
}

void sendstring(u8 chan, char* s)
{
    while(*s)
{
  
switch(chan)
{
case 1:
fputc(*s++);
break;
case 2:
fputc2(*s++);
break;
default:
fputc(*s++);
break;
}
}
   
}

EXTI0_IRQHandler()
{
   fputc2('1');
   EXTI->PR|= 1<<0;  //挂起寄存器,写入1 清除
}

int main (void)
 { 
 
clk4_init();
XGZ_NVIC_PriorityGroupConfig(2); //为系统配置中断分组2

gpio4_init();

Usart1_Config(9600);

usart2_init(168,9600);

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

 RCC->APB2ENR |= (1<<2)|(1<<0);
//GPIOA->CRL &=0xFFFFFFF0;
//GPIOA->CRL |=0x00000008;

 GPIOA->MODER &=~ (3<<0);
GPIOA->MODER |= (2<<0);
GPIOA->PUPDR &=~ (3<<0);

GPIOA->ODR |=1<<0;
   

NVIC->ISER[0] |= 1<<6;     //
EXTI->IMR|= 1<<0;        //0 线 中断使能
EXTI->FTSR= 1<<0;       // 下降沿触发使能
//AFIO->EXTICR[0] |=0;   //PA0

sendstring(1, "This is uart1 test.");
sendstring(2, "This is uart2 test.");
 while (1)
{
if(GPIOD->IDR & 0x01)
{
GPIOD->ODR |= (1<<8)|(1<<9);   
delay(100);
}
GPIOD->ODR &= ~((1<<8)|(1<<9));   
delay(100);

}


 }   



 

 运行结果,两个串口都收发正常:

 

 调试界面

 

标签:GCC,1CR1,tx,NVIC,Proteus,串口,usart2,Channel
From: https://www.cnblogs.com/xgz21/p/17065785.html

相关文章

  • s3c2440 gcc交叉编译工具链制作
    前言:因为准备学习lwip,之前在stm32上移植跟着教程走得差不多了,但是用的keil。所以想在一个新平台移植一下巩固巩固,正好有2440的板子,也学一下gcc-make这样的开发流程,配套的......
  • 振弦采集模块配置工具VMTool通用串口调试模块
    振弦采集模块配置工具VMTool通用串口调试模块VMTool扩展功能双击主界面右侧扩展工具条可实现扩展功能区的显示与隐藏切换。扩展功能包括串口调试、MODBUS、实时曲线及数......
  • 串口调试助手的数据保存问题
    提问: 本人在做FPGA的数据采集系统,采集到的数据通过串口发送给串口调试助手,现在用的是可以显示波形的VOFA+,但是保存数据为csv文件时,数据只有一列,不像示波器保存的数据有时......
  • LINUX-QT串口操作
    接收显示控件   添加串口模块  添加头文件  在头文件中声明一个函数  使用声明的函数  在mainwindow中进行调用InitUI  voidMainW......
  • [RK356x] [Firefly-Ubuntu] 1min教你在Ubuntu系统安装GCC开发工具
    ​​Firefly​​​提供的​​Ubuntu​​​系统并没有预装​​GCC​​​开发工具,接下来我带领大家安装这个工具,并结合两个例子简单使用​​GCC​​!文章目录​​一、GGC安装​......
  • Arduino之读取温度传感器并串口显示
    温度是我们经常接触到的物理量,能够被我们所直观的感受得到,例如天气凉了需要增添衣物,吃的食物太烫需要吹一吹,同时也需要对温度精确的测量,例如人类的正常体温是37.5℃,一个大气......
  • 振弦采集模块配置工具VMTool通用串口调试模块
    振弦采集模块配置工具VMTool通用串口调试模块VMTool扩展功能双击主界面右侧扩展工具条可实现扩展功能区的显示与隐藏切换。扩展功能包括串口调试、MODBUS、实时曲线及数......
  • Linux 驱动像单片机一样读取一帧dmx512串口数据
    硬件全志R528目标:实现Linux 读取一帧dmx512串口数据。问题分析:因为串口数据量太大,帧与帧之间的间隔太小。通过Linux自带的读取函数方法无法获取到帧头和帧尾,读取到的数......
  • Fedora38的新改进:GCC 工具链更新计划
    GCC13是一个重大更新,引入了Rust和Modula-2语言前端、AMDZen4“znver4”支持、其他新的CPUtarget、添加的各种C和C++语言功能,以及大量其他更新。Fed......
  • linux 安裝gcc
    linux安裝gcchttps://blog.csdn.net/lydong_/article/details/79812402   改成sudo......   nopackagegccavailableubuntu18.04repo安装https://blo......