1.延时函数
- 空代码延时 执行一条指令的时间是:1/32M
//代码延时 执行一条指令的时间是:1/32Mvoid delay_S(uint32_t s){ for(int i=0;i<s;i++) { for(long j=0;j<32000000;j++) { __asm__("nop"); //空指令 } }}
- delay_cycles
假设主频为 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.外部中断
- 对于按键外部中断的配置(PA18,通道0)
事件管理器传输的事件包括:
- 作为中断请求 (IRQ) 传输到 CPU 的外设事件(静态事件) – 示例:GPIO 中断会发送到 CPU作为 DMA 触发器传输到 DMA 的外设事件(DMA 事件) – 示例:传输到 DMA、请求 DMA 传输的 UART 数据接收触发器传输到另一个外设以直接触发硬件中操作的外设事件(通用事件) – 示例:TIMx 计时器外设将周期性事件发布到 ADC 订阅者端口,ADC 使用该事件触发采样开始(ADC触发采样)
- 配置静态事件时,通用事件配置为通道0
静态事件(类似于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)
- 按键配置为通道2
将lED配置为对应的通道,设置为触发后翻转
配置为通用事件外部中断后就不用在初始化时开启中断使能了
NVIC_EnableIRQ(GPIOA_INT_IRQn);
4. 串口
- 串口0,1,2都在PD0总线上,可以看到在时钟是ULPCLK串口3在PD1总线上
- 硬件流控制主要通过这个RTS和CTS控制,为了同步时钟,一般不开启
发送两种
- 串口发送字符串两种实现方式‘第一种正常
DL_UART_transmitData
//串口发送单个字符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
能自动检测发送寄存器是否已被填充(推荐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_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 输出单稳态模式
通用计时器 (TIMAx) 的具体特性包括:(类似高级定时器)
- 具有重复重新加载模式的 16 位递减或加减计数器可选和可配置的时钟源用于对计数器时钟频率进行分频的 8 位可编程预分频器重复计数器,仅在计数器的给定周期数之后生成中断或事件最多四个独立通道,用于:
- – 输出比较– 输入捕捉– 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呼吸灯实验
/*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
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电压基准。
单通道单次手动触发(没实现)
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; }}