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;
  }
}
 
相关推荐
whysqwhw2 小时前
js之Promise
前端
恋猫de小郭5 小时前
Flutter 3.35 发布,快来看看有什么更新吧
android·前端·flutter
chinahcp20086 小时前
CSS保持元素宽高比,固定元素宽高比
前端·css·html·css3·html5
gnip7 小时前
浏览器跨标签页通信方案详解
前端·javascript
gnip8 小时前
运行时模块批量导入
前端·javascript
hyy27952276848 小时前
企业级WEB应用服务器TOMCAT
java·前端·tomcat
逆风优雅8 小时前
vue实现模拟 ai 对话功能
前端·javascript·html
若梦plus9 小时前
http基于websocket协议通信分析
前端·网络协议
不羁。。9 小时前
【web站点安全开发】任务3:网页开发的骨架HTML与美容术CSS
前端·css·html
这是个栗子9 小时前
【问题解决】Vue调试工具Vue Devtools插件安装后不显示
前端·javascript·vue.js