掘金 人工智能 07月21日 14:02
mspm0系列入门——基础
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文档详细介绍了MSP430微控制器(MSP430G3507)的各项核心功能和编程实践。内容涵盖了延时函数的实现(空指令、周期延时、滴答定时器阻塞与中断延时)、按键检测(轮询与外部中断)、串口通信(发送、接收、重定向)、定时器配置(通用计时器、高级计时器)、PWM输出、ADC采样(单次、多通道、中断触发)以及DMA和电机驱动(编码器、输入捕获)的应用。通过代码示例和原理讲解,为开发者提供了全面的参考。

⏱️ **延时函数实现**:提供了多种延时方式,包括基于空指令的简单延时 `delay_S`,基于CPU时钟周期的 `delay_cycles`,以及利用滴答定时器实现的阻塞式 `delay_ms` 和中断式延时。中断式延时通过全局变量和中断服务函数实现非阻塞的延时功能。

👆 **按键与中断处理**:介绍了按键的轮询检测方法,通过延时消抖来提高稳定性。同时深入阐述了外部中断的配置,包括静态事件和通用事件的两种模式,并提供了GPIO外部中断服务函数的实现,用于响应按键事件并触发相应操作,如LED状态切换。

💬 **串口通信详解**:详细讲解了串口的发送(单字符、字符串、阻塞式发送)和接收(中断接收、数据处理)方法。通过 `fputc`、`fputs`、`puts` 函数的重定向,实现了标准输出到串口的功能,方便调试。特别强调了串口中断标志位清除的注意事项。

⚙️ **定时器与PWM应用**:阐述了通用计时器(TIMGx)和高级计时器(TIMAx)的特性,包括计数器模式、预分频器、通道功能(输出比较、输入捕获、PWM)。重点介绍了PWM的互补输出功能,并通过“呼吸灯”实验展示了如何通过调整PWM周期和占空比来控制LED亮度。

📊 **ADC采样与触发**:详细介绍了MSPM0G3507的12位ADC特性,包括支持的转换模式(单次、重复、多通道顺序)、分辨率、采样率和电压基准选择。提供了ADC单通道单次手动触发、多通道单次手动触发以及中断触发的实现示例,并展示了如何处理ADC转换结果。

🚀 **电机驱动与编码器**:深入介绍了编码器在电机驱动中的应用,包括编码器的初始化、计数值获取、方向判断以及通过GPIO外部中断处理A相和B相信号,实现高精度电机转速和方向的测量。此外,还提及了利用定时器的输入捕获功能进行测速。

1.延时函数

//代码延时 执行一条指令的时间是:1/32Mvoid delay_S(uint32_t s){    for(int i=0;i<s;i++)    {        for(long j=0;j<32000000;j++)        {                __asm__("nop");  //空指令        }          }}
假设主频为 32MHz,想延时 1 微秒:(主频已经设置为80MHz)-   1 微秒 = 1e-6 秒-   32MHz = 32,000,000 Hz-   1 微秒内有 32 个时钟周期、delay_cycles(32); // 大约延时 1 微秒
滴答定时器延时阻塞版   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;         }      }      }
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.按键

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.外部中断

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

静态事件(类似于32的外部中断)

#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)

NVIC_EnableIRQ(GPIOA_INT_IRQn);

4. 串口

发送两种

//串口发送单个字符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++);    }
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);
//串口重定向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);    //使能串口中断    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)

配置

6.PWM

/*test_pwm pwm*/#define pwm_period   1000static 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

ADC基本参数

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

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);    }}

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

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);    }}

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

/*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外部中断

#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);}

输入捕获

/*左电机的编码器测速*/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;  }} 

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

微控制器 MSP430 嵌入式开发 延时函数 外部中断 串口通信 定时器 PWM ADC 编码器 电机驱动
相关文章