mspm0系列入门——基础

1.延时函数

  • 空代码延时 执行一条指令的时间是:1/32M
js 复制代码
//代码延时 执行一条指令的时间是:1/32M
void delay_S(uint32_t s)
{
    for(int i=0;i<s;i++)
    {

        for(long j=0;j<32000000;j++)
        {
                __asm__("nop");  //空指令
        }      
    }
}
  • delay_cycles
js 复制代码
假设主频为 32MHz,想延时 1 微秒:(主频已经设置为80MHz)

-   1 微秒 = 1e-6 秒
-   32MHz = 32,000,000 Hz
-   1 微秒内有 32 个时钟周期、

delay_cycles(32); // 大约延时 1 微秒
  • 滴答定时器延时阻塞版
js 复制代码
滴答定时器延时阻塞版  
 void delay_ms(uint32_t ms)
 {
      uint32_t ticks=ms*(CPUCLK_FREQ/1000);
      uint32_t count_new=0,count_old=0;
      uint32_t count;
      count_old= SysTick->VAL;
      while (1) 
      {
         count_new=SysTick->VAL;
         if(count_old!=count_new)
         {
             if(count_new<count_old)
             {
                 count =    count+(count_old-count_new)  ;
             }
             else if(count_new>count_old)
             {
                 count =    count+ SysTick->LOAD- count_new+count_old;
             }
             count_old=count_new;
             if(count>=ticks) return;
         }
      }  
   
 }
  • 滴答定时器中断延时
js 复制代码
volatile uint32_t delay_vlaue;
void delay_ms(uint32_t ms)
{
    delay_vlaue=ms;
    while(delay_vlaue!=0);
}
void SysTick_Handler(void)
{
        delay_vlaue--;
}

2.按键

js 复制代码
int main(void)
{
    SYSCFG_DL_init();
    while (1) 
    {
        //1秒周期
         delay_ms(200);
            if(DL_GPIO_readPins(key_PORT,key_PIN_A18_PIN)!=0)
            {
                delay_ms(30); 
                if(DL_GPIO_readPins(key_PORT,key_PIN_A18_PIN)!=0)
                {
                    DL_GPIO_togglePins(LED_PORT, LED_PIN_A14_PIN);
                }
            }
    }
}

3.外部中断

  • 对于按键外部中断的配置(PA18,通道0)

事件管理器传输的事件包括:

  • 作为中断请求 (IRQ) 传输到 CPU 的外设事件(静态事件) -- 示例:GPIO 中断会发送到 CPU
  • 作为 DMA 触发器传输到 DMA 的外设事件(DMA 事件) -- 示例:传输到 DMA、请求 DMA 传输的 UART 数据接收触发器
  • 传输到另一个外设以直接触发硬件中操作的外设事件(通用事件) -- 示例:TIMx 计时器外设将周期性事件发布到 ADC 订阅者端口,ADC 使用该事件触发采样开始(ADC触发采样)
  • 配置静态事件时,通用事件配置为通道0
静态事件(类似于32的外部中断)
  • 只需要对按键配置中断,不用开启通道
js 复制代码
#include "ti_msp_dl_config.h"
 
//滴答定时器中断延时
volatile uint32_t delay_vlaue;
void delay_ms(uint32_t ms)
{
    delay_vlaue=ms;
    while(delay_vlaue!=0);
}
void SysTick_Handler(void)
{
        delay_vlaue--;
}
uint16_t test_data1=0;
 
//GPIO外部中断函数,静态事件
void GROUP1_IRQHandler(void)
{
   
     // 判断是否为GPIOA引脚产生的中断
    if(DL_Interrupt_getStatusGroup(DL_INTERRUPT_GROUP_1,DL_INTERRUPT_GROUP1_GPIOA))   
    {
        
        if(DL_GPIO_readPins(key_PORT,key_PIN_A18_PIN)>0)
        {
           test_data1++;
            // 切换LED状态
            DL_GPIO_togglePins(LED_PORT, LED_PIN_A14_PIN);
        }
          // 清除GPIOA引脚的中断挂起标志,避免重复触发     
       DL_Interrupt_clearGroup(DL_INTERRUPT_GROUP_1,DL_INTERRUPT_GROUP1_GPIOA);
    }
}
int main(void)
{
    SYSCFG_DL_init();

    NVIC_EnableIRQ(GPIOA_INT_IRQn);//开启按键引脚的GPIOA端口中断

    while (1) 
    {
          
    }
}
通用事件(new)
  • 按键配置为通道2
  • 将lED配置为对应的通道,设置为触发后翻转

  • 配置为通用事件外部中断后就不用在初始化时开启中断使能了

NVIC_EnableIRQ(GPIOA_INT_IRQn);

4. 串口

  • 串口0,1,2都在PD0总线上可以看到在时钟是ULPCLK
  • 串口3在PD1总线上
  • 硬件流控制主要通过这个RTS和CTS控制,为了同步时钟,一般不开启

发送两种

  • 串口发送字符串两种实现方式'
  • 第一种正常DL_UART_transmitData
js 复制代码
//串口发送单个字符
void uart0_send_char(char ch)
{
    //当串口0忙的时候等待,不忙的时候再发送传进来的字符
    while( DL_UART_isBusy(UART_0_INST) == true );
    //发送单个字符
    DL_UART_transmitData(UART_0_INST, ch);
}
//串口发送字符串
void uart0_send_string(char* str)
{
    //当前字符串地址不在结尾 并且 字符串首地址不为空
    while(*str!=0&&str!=0)
        //发送字符串首地址中的字符,并且在发送完成之后首地址自增
        uart0_send_char(*str++);
    
}
  • 第二种DL_UART_transmitDataBlocking自动检测发送寄存器是否已被填充(推荐
js 复制代码
void uart0_send_string(char* str)
{
    //当前字符串地址不在结尾 并且 字符串首地址不为空
    while(*str!=0&&str!=0)
   // while(*str!='\0')
        //发送字符串首地址中的字符,并且在发送完成之后首地址自增
        DL_UART_transmitDataBlocking(UART_0_INST,*str++);
    
}

char str[50];


    sprintf(str,"res_sin:%.2f\r\n", res);
    uart0_send_string(  str);
    delay_cycles(CPUCLK_FREQ);
  • 串口重定向函数(都要写
js 复制代码
//串口重定向
int fputc(int ch, FILE *stream)
{
         
    DL_UART_transmitDataBlocking(UART_0_INST,ch);
    return ch;
}

int fputs(const char *ptr, FILE *stream)
{
     uint16_t i,len;
     len=strlen(ptr);
     for( i=0;i<len;i++)
     {
        DL_UART_transmitDataBlocking(UART_0_INST,ptr[i]);
     }
    return len;
}

int puts(const char *ptr)
{
    int count=fputs(ptr, stdout);
    count+=fputs("\n",stdout);
    return count;
}

接收

  • 开中断
    • 需要注意串口清除中断标志位函数不用在中断里
    • !!!NVIC_ClearPendingIRQ(UART_0_INST_INT_IRQN);这句一定不能加!!!
js 复制代码
//在初始化里加上
     //清除串口中断标志
    NVIC_ClearPendingIRQ(UART_0_INST_INT_IRQN);
    //使能串口中断
    NVIC_EnableIRQ(UART_0_INST_INT_IRQN);
    
    
//串口的中断服务函数
char rx_buffer[100];
uint8_t buffer_index;
uint8_t buffer_full;
void UART_0_INST_IRQHandler(void)
{
    //如果产生了串口中断
    if(DL_UART_getPendingInterrupt(UART_0_INST)==DL_UART_IIDX_RX)
    {
        int rx_data =  DL_UART_receiveDataBlocking(UART_0_INST);
         if(rx_data=='\r'||rx_data=='\n')
		    {
                rx_buffer[buffer_index] = '\0';  
                  buffer_full = 1;  //串口接口函数当数据接收完成,可以用buffer_full来处理串口接收数据
                  buffer_index = 0;  
		    }
             else if(buffer_index< sizeof(rx_buffer) - 1)
		    {
			    rx_buffer[buffer_index++]=rx_data;
	    	}
            //将保存的数据再发送出去
            DL_UART_transmitDataBlocking(UART_0_INST,rx_data);
    }

}

5.定时器(8.29)

  • 通用计时器 (TIMGx) 的具体特性包括:(类似通用定时器

    • 具有重复重新加载模式的 16 位递增、递减或递增/递减计数器
    • 具有重复重新加载模式的 32 位递增、递减或递增/递减计数器
    • 可选和可配置的时钟源
    • 用于对计数器时钟频率进行分频的 8 位可编程预分频器
    • 两个独立通道,用于:
      • 输出比较
      • 输入捕捉
      • PWM 输出
      • 单稳态模式
    • CC 寄存器在 TIMG7 和 TIMG12 中可用
    • 用于加载的影子寄存器在 TIMG7 中可用
    • 霍尔传感器输入的交叉触发事件逻辑
    • 支持中断/DMA 触发生成以及跨外设(例如 ADC)触发功能
  • 通用计时器 (TIMAx ) 的具体特性包括:(类似高级定时器

    • 具有重复重新加载模式的 16 位递减或加减计数器
    • 可选和可配置的时钟源
    • 用于对计数器时钟频率进行分频的 8 位可编程预分频器
    • 重复计数器,仅在计数器的给定周期数之后生成中断或事件
    • 最多四个独立通道,用于:
      • -- 输出比较
      • -- 输入捕捉
      • -- PWM 输出
      • -- 单稳态模式
    • 用于加载的影子寄存器和 CC 寄存器在 TIMA0 和 TIMA1 都可用
    • 互补输出 PWM
    • 具有可编程死区插入功能的非对称 PWM
配置
  • 区别于stm32,g3507的定时器周期是自己写的,不用配置arr,只用配置PSC(arr他会自己计算)
  • frq=4MHz/1/100/40000=1HZ

6.PWM

  • timA才有互补输出功能,IIMG没有互补输出功能(结尾带N的就是互补输出,例如当TIMA0_C3输出高电平时TIMA0_C3N就输出低电平)

    • 互补输出:就是同时输出两个PWM,极性相反
  • timA有4个通道输出,IIMG只有两个通道输出

  • PWM呼吸灯实验

js 复制代码
/*test_pwm pwm*/

#define pwm_period   1000

static const DL_TimerG_PWMConfig gPWM_0Config = {
    .pwmMode = DL_TIMER_PWM_MODE_EDGE_ALIGN,//边沿触发
    .period = pwm_period,//周期
    .isTimerWithFourCC = false,
    .startTimer = DL_TIMER_START,
};

int main(void)
{
    SYSCFG_DL_init();

    //清除串口中断标志
    NVIC_ClearPendingIRQ(UART_0_INST_INT_IRQN);
    //使能串口中断
    NVIC_EnableIRQ(UART_0_INST_INT_IRQN);
    //使能定时器中断 TIMA0
    NVIC_EnableIRQ(TIMER_0_INST_INT_IRQN);

    uart0_send_string("start pwm hooxi led test\r\n");
    
    //PWM周期调整,通过结构体调节 TIMG6
    DL_TimerG_initPWMMode( PWM_0_INST, (DL_TimerG_PWMConfig *) &gPWM_0Config);

     while (1)
    {
       //delay_ms(50);
        for(int i=pwm_period;i>0;i--)
        {
             DL_Timer_setCaptureCompareValue(PWM_0_INST,i,DL_TIMER_CC_0_INDEX);
             delay_ms(1);
        }
      for(int j=0;j<pwm_period;j++)
        {
                DL_Timer_setCaptureCompareValue(PWM_0_INST,j,DL_TIMER_CC_0_INDEX);
              delay_ms(1);
        }
    }
}

7.ADC

  • MSPM0G3507采用的是逐次逼近型的12位ADC ,它有17个多路复用通道可以转换。17个外部通道,都对应单片机的某个引脚,这个引脚不是固定的,详情请参考引脚图或者数据手册。

  • 各种通道的 A/D 转换可以配置成 单次、序列转换 模式。

    • 单次转换模式: 每次进行一次ADC转换后,ADC会自动停止,并将结果存储在ADC数据寄存器中。

    • 重复单次转换模式: 当ADC完成一次转换后,它会自动启动另一次转换,持续的进行转换,直到外部触发或者软件触发的方式停止连续转换。

    • 多通道顺序单次转换模式: 用于对多个输入通道进行依次转换。在该模式下,ADC会根据配置的通道采集顺序,对多个通道进行单次采样并转换。

    • 多通道顺序重复转换模式: 用于对多个输入通道进行依次重复转换。在该模式下,ADC会根据配置的通道采集顺序,对多个通道进行重复采样并转换。

ADC基本参数

  • 分辨率: 表示ADC转换器的输出精度,通常以位数(bit)表示,比如8位、10位、12位等,位数越高,精度越高。MSPM0L1306支持8、10、12位的分辨率。

  • 采样率: 表示ADC对模拟输入信号进行采样的速率,通常以每秒采样次数(samples per second,SPS)表示,也称为转换速率,表示ADC能够进行多少次模拟到数字的转换。MSPM0G3507的SPS为4Msps(尽量不要超过)。

  • 电压基准: ADC的电压基准是用于与模拟输入信号进行比较,从而实现模拟信号到数字信号的转换的一个参考电压。这个基准电压的准确性和稳定性对ADC的转换精度有着决定性的影响。而MSPM0G3507可以支持软件选择三种基准:

    • (1)1.4V 和 2.5V 的可配置内部专用 ADC 基准电压 (VREF)
    • (2)MCU 电源电压 (VDD) (一般是这个)
    • (3)通过 VREF+和 VREF- 引脚为 ADC 提供外部基准。
    • 如未配置电压基准则默认使用MCU电源电压作为ADC电压基准。

单通道单次手动触发(没实现)

js 复制代码
float adc_getvalue(void)
{
    uint16_t adc_value=0;
    //开启adc转换
    DL_ADC12_startConversion(ADC12_0_INST);
    //如果adc在忙,则卡住
    while (DL_ADC12_getStatus(ADC12_0_INST)!=DL_ADC12_STATUS_CONVERSION_IDLE);
    //暂停adc转换
    DL_ADC12_stopConversion(ADC12_0_INST)  ;
    //获取存储在内存0的转换结果
    adc_value=DL_ADC12_getMemResult(ADC12_0_INST,   DL_ADC12_MEM_IDX_0);
    //开启adc转换,因为单次转换会导致采集停止
    DL_ADC12_enableConversions( ADC12_0_INST);

    return adc_value*3.3f/4095;
}


int main(void)
{
    SYSCFG_DL_init();

    //清除串口中断标志
    NVIC_ClearPendingIRQ(UART_0_INST_INT_IRQN);
    //使能串口中断
    NVIC_EnableIRQ(UART_0_INST_INT_IRQN);
    
    uart0_send_string("adc test \r\n");                                                   
    while (1)
    {
        
    adc_data=adc_getvalue();
    
    sprintf(str, "adc_value :%.2f\r\n", adc_data);
    //printf("adc_value :%.2f\r\n",adc_data);
    uart0_send_string(str);

    delay_ms(500);

    }
}

多通道单次手动触发(没实现)

js 复制代码
float adc_data[2]={0};
void  adc_getvalue(void)
{
    //开启adc转换
    DL_ADC12_startConversion(ADC12_0_INST);
    //如果adc在忙,则卡住
    while (DL_ADC12_getStatus(ADC12_0_INST)!=DL_ADC12_STATUS_CONVERSION_IDLE);
    //暂停adc转换,否则会卡住
   //获取存储在内存0的转换结果 
     DL_ADC12_stopConversion(ADC12_0_INST) ;
    adc_data[0]=(DL_ADC12_getMemResult(ADC12_0_INST,   DL_ADC12_MEM_IDX_0)*3.3f/4095.0f);
    adc_data[1]=(DL_ADC12_getMemResult(ADC12_0_INST,   DL_ADC12_MEM_IDX_1)*3.3f/4095.0f);
    //开启adc转换,因为单次转换会导致采集停止
    DL_ADC12_enableConversions( ADC12_0_INST);
}
int main(void)
{
    SYSCFG_DL_init();
    //清除串口中断标志
    NVIC_ClearPendingIRQ(UART_0_INST_INT_IRQN);
    //使能串口中断
    NVIC_EnableIRQ(UART_0_INST_INT_IRQN);
    uart0_send_string("adc test \r\n");                                                                   
    while (1)
    {
     adc_getvalue();
     sprintf(str, "adc_value1 :%.2f  adc_value2 :%.2f\r\n", adc_data[0],adc_data[1]);
    //printf("adc_value :%.2f\r\n",adc_data);
     uart0_send_string(str);
     delay_ms(500);
    }
}

中断单通道定时器触发(实现)

js 复制代码
/*test  ADC_interrupt*/


volatile bool ADC_Flag;//中断标志位
volatile uint16_t ADC_Val;//采样值

int main(void)
{
    SYSCFG_DL_init();
    //清除串口中断标志
    NVIC_ClearPendingIRQ(UART_0_INST_INT_IRQN);
    //使能串口中断
    NVIC_EnableIRQ(UART_0_INST_INT_IRQN);

    NVIC_EnableIRQ(ADC12_0_INST_INT_IRQN);

    uart0_send_string("ADC_interrupt test \r\n");                                                                   
    while (1)
    {
        ADC_Flag=false ;
        DL_ADC12_startConversion(ADC12_0_INST);//开始采样变换
            while(ADC_Flag==false);//等待转换完成,如果退出,说明已经弄好了
        ADC_Val= DL_ADC12_getMemResult(ADC12_0_INST, DL_ADC12_MEM_IDX_0);//读取结果

        DL_ADC12_enableConversions(ADC12_0_INST);//再次使能中断,因为每次执行完就关闭了

        sprintf(str, "adc_value1 :%.2f \r\n", ADC_Val*3.3f/4095.0f );
         uart0_send_string(str);

     delay_ms(500);
    }
}
 void ADC12_0_INST_IRQHandler (void)
{
     switch (DL_ADC12_getPendingInterrupt(ADC12_0_INST)) 
    {
        case DL_ADC12_IIDX_MEM0_RESULT_LOADED:
            ADC_Flag = true;
            break;
        default:
            break;
    }
}

DMA

天猛星最上面spi的基本引脚

1.电机驱动

jlc编码器(gpio外部中断

js 复制代码
#ifndef __ENCODER_H_
#define __ENCODER_H_

#include "ti_msp_dl_config.h"
#include "main_IO.h"


typedef  enum
{
    FORWARD,    // 正向
    REVERSAL    // 反向
} ENCODER_DIR;
typedef struct {
    volatile long long temp_count; //保存实时计数值
    int count;         						 //根据定时器时间更新的计数值
    ENCODER_DIR dir;            	 //旋转方向
} ENCODER_RES;

void encoder_init(void);
int get_encoder_count(void);
ENCODER_DIR get_encoder_dir(void);
void encoder_update(void);
void GROUP1_IRQHandler(void);
#endif





#include "encoder.h"
static ENCODER_RES motor_encoder;
//编码器初始化
void encoder_init(void)
{
	//编码器引脚外部中断
	NVIC_ClearPendingIRQ(GPIOB_INT_IRQn);
	NVIC_EnableIRQ(GPIOB_INT_IRQn);
}

//获取编码器的值
int get_encoder_count(void)
{
	return motor_encoder.count;
}

//获取编码器的方向
ENCODER_DIR get_encoder_dir(void)
{
	return motor_encoder.dir;
}

//编码器数据更新
//请间隔一定时间更新
void encoder_update(void)
{
	motor_encoder.count = motor_encoder.temp_count;

	//确定方向
	motor_encoder.dir = ( motor_encoder.count >= 0 ) ? FORWARD : REVERSAL;

	motor_encoder.temp_count = 0;//编码器计数值清零
}

//外部中断处理函数
void GROUP1_IRQHandler(void)
{
	uint32_t gpio_status;

	//获取中断信号情况
	gpio_status = DL_GPIO_getEnabledInterruptStatus(GPIO_ENCODER_PORT, GPIO_ENCODER_PIN_A_PIN | GPIO_ENCODER_PIN_B_PIN);
	//编码器A相上升沿触发
	if((gpio_status & GPIO_ENCODER_PIN_A_PIN) == GPIO_ENCODER_PIN_A_PIN)
	{
		//如果在A相上升沿下,B相为低电平
		if(!DL_GPIO_readPins(GPIO_ENCODER_PORT,GPIO_ENCODER_PIN_B_PIN))
		{
			motor_encoder.temp_count--;
		}
		else
		{
			motor_encoder.temp_count++;
		}
	}//编码器B相上升沿触发
	else if((gpio_status & GPIO_ENCODER_PIN_B_PIN)==GPIO_ENCODER_PIN_B_PIN)
	{
		//如果在B相上升沿下,A相为低电平
		if(!DL_GPIO_readPins(GPIO_ENCODER_PORT,GPIO_ENCODER_PIN_A_PIN))
		{
			motor_encoder.temp_count++;
		}
		else
		{
			motor_encoder.temp_count--;
		}
	}
	//清除状态
	DL_GPIO_clearInterruptStatus(GPIO_ENCODER_PORT,GPIO_ENCODER_PIN_A_PIN|GPIO_ENCODER_PIN_B_PIN);
}

输入捕获

js 复制代码
/*左电机的编码器测速*/
void TIMG7_IRQHandler(void)
{
  switch (DL_TimerG_getPendingInterrupt(ENCODER_L_INST)) 
{
  case DL_TIMERG_IIDX_CC0_DN:
   
  uint16_t L_run_dierct = DL_GPIO_readPins(GPIO_Encoder_PORT, GPIO_Encoder_PIN_Front_Left_B_PIN);//读取IO电平获取电机旋转方向
   
   if(L_run_dierct) control.speed.left_encoder_run--;
   else           control.speed.left_encoder_run++;
   
     break;
  default:
    break;
  }
}
 
/*右电机的编码器测速*/
void TIMG12_IRQHandler(void)
{
  switch (DL_TimerG_getPendingInterrupt(ENCODER_R_INST)) 
    {
  case DL_TIMERG_IIDX_CC0_DN:
   
  uint16_t R_run_dierct = DL_GPIO_readPins(GPIO_Encoder_PORT, GPIO_Encoder_PIN_Front_Right_B_PIN);//读取IO电平获取电机旋转方向
   if(R_run_dierct) control.speed.right_encoder_run--;
   else control.speed.right_encoder_run++; 
     break;
  default:
    break;
  }
}
 
相关推荐
断竿散人1 分钟前
前端救急实战:用 patch-package 解决 vue-pdf 电子签章不显示问题
前端·webpack·npm
蓝倾2 分钟前
淘宝获取商品分类接口操作指南
前端·后端·fastapi
十盒半价4 分钟前
深入理解 React 中的 useState:从基础到进阶
前端·react.js·trae
ccc10185 分钟前
前端性能优化实践:深入理解懒加载的实现与最佳方案
前端
CodeTransfer7 分钟前
今天给大家搬运的是四角线框hover效果
前端·vue.js
归于尽8 分钟前
别让类名打架!CSS 模块化教你给样式上 "保险"
前端·css·react.js
凤凰AI35 分钟前
Python知识点4-嵌套循环&break和continue使用&死循环
开发语言·前端·python
Lazy_zheng1 小时前
虚拟 DOM 到底是啥?为什么 React 要用它?
前端·javascript·react.js
多啦C梦a1 小时前
前端按钮大撞衫,CSS 模块化闪亮登场!
前端·javascript·面试
拾光拾趣录1 小时前
WebRTC深度解析:从原理到实战
前端·webrtc