Stm32CubeMX学习笔记 |
您所在的位置:网站首页 › stm32两个串口 › Stm32CubeMX学习笔记 |
本文内容结合个人开发经验和其他博主的见解,供个人学习使用 ----待更新---- 文章目录 ----待更新----系统选择芯片配置时钟、调试模式√中断优先级NVIC√生成code√ GPIO -外部中断定时器定时器中断√PWM模式√编码器模式√ ADC单通道轮询√多通道轮询√ 串口√串口发送串口接收中断 系统 选择芯片 配置时钟、调试模式√1.设置使用外部高速时钟源(8M晶振) 如果使用RTC时钟,要使能LSE。 说明: BYPASS Clock Source(旁路时钟源) Crystal/Ceramic Resonator(石英/陶瓷 晶振) 2.设置时钟树为72M 修改 HCLK 的值为 72 后,输入回车,软件会自动修改所有配置 3.调试模式为SW模式 中断优先级NVIC√NVIC即嵌套向量中断控制器,用于配置各种中断源的使能和失能,响应优先级。 除了在此处配置,在具体中断功能中的NVICsetting选项卡中也可以设置并自动保持同步。 Preemption Priority:抢占优先级 Sub Priority:子优先级(响应优先级) 数字越小表示优先级越高 在CubeMX中默认只有抢占优先级可以进行0-15的设置,响应优先级只能为0,可以自行设置中断优先级控制位分组,分配如下: 编号分配情况说明0/1/2/34:016抢占优先级,0 子优先级43:18 抢占优先级,2 子优先级52:24 抢占优先级,4 子优先级61:32 抢占优先级,8 子优先级70:40 抢占优先级,16子优先级多个中断同时响应时,抢占优先级高的会先于抢占优先级低的执行;如果抢占优先级相同,就比较响应优先级;如果响应优先级也相同,就比较它们的硬件中断编号,编号越小优先级越高。 如果在此之前还有一个响应优先级低的还在进行,那么高的响应优先级必须等待低的先进行。 生成code√1.工程名称,路径,编译器设置 注意工程名和路径要避免使用中文 2.代码生成设置 GPIO -GPIO 8 种工作模式 GPIO_Mode_AIN 模拟输入 GPIO_Mode_IN_FLOATING 浮空输入 GPIO_Mode_IPD 下拉输入 GPIO_Mode_IPU 上拉输入 GPIO_Mode_Out_OD 开漏输出 GPIO_Mode_Out_PP 推挽输出 GPIO_Mode_AF_OD 复用开漏输出 GPIO_Mode_AF_PP 复用推挽输出1、上拉输入、下拉输入可以用来检测外部信号;例如,按键等; 2、浮空输入模式,由于输入阻抗较大,一般把这种模式用于标准通信协议的I2C、USART 的接收端; 3、普通推挽输出模式一般应用在输出电平为 0 和 3.3V 的场合。 普通开漏输出模式一般应用在电平不匹配的场合,如需要输出 5V 的高电平,就需要在外部一个上拉电阻,电源为 5V,把 GPIO 设置为开漏模式,当输出高阻态时,由上拉电阻和电源向外输出 5V 电平。 4、对于相应的复用模式(复用输出来源片上外设),则是根据 GPIO 的复用功能来选择,如 GPIO 的引脚用作串口的输出(USART/SPI/CAN),则使用复用推挽输出模式。如果用在 I2C、SMBUS 这些需要线与功能的复用场合,就使用复用开漏模式。 5、在使用任何一种开漏模式时,都需要接上拉电阻。 业务代码 //读取电平状态 GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin); //设置引脚状态 void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState); //转换引脚状态 void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin); //锁定引脚状态 HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin); //举例: //读取PA0引脚状态 HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0); //将PA0引脚状态改为低电平 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);user lable位于main.h 外部中断 定时器 定时器中断√1.配置定时器 中断间隔T = (arr+1)(psc+1) / 72M 其中72M 为时钟树设置的主频 2.开启中断 高级定时器勾选update interrupt,普通定时器勾选global interrupt 这里在系统NVIC中配置也是可以的 3.业务代码 在主函数添加初始化代码 //__HAL_TIM_CLEAR_FLAG(&htim1,TIM_FLAG_UPDATE);//如果不想使能中断后立即进入中断,加上这一句 HAL_TIM_Base_Start_IT(&htim1);//开启定时器1中断重写中断回调函数 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == htim1.Instance) { } }完成! PWM模式√频率 = 定时器时钟 / (psc预分频 + 1)/ (arr计数值 + 1)Hz 占空比 = ccr ( 对比值) / (arr计数值)% 驱动电机的频率一般10K左右,太低,电机有噪音;太高,功率管无法承受且给电路板带来高频干扰。占空比控制精度一般1000合适。 PWM模式1,有效电平为高,即直观上的ccr体现占空比 1.PWM配置 无需中断,不需要配置中断优先级 2.业务代码 // 使能tim8的通道 HAL_TIM_PWM_Start(&htim8,TIM_CHANNEL_1); // 修改tim8的通道1的pwm比较值为pwmval,即修改占空比 __HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_1, pwmval);完成! 编码器模式√编码器为AB相,用两路输入,编码模式设置为 Encoder Mode TI1 and TI2 则会默认检测AB相的上升沿与下降沿。每一个上升沿和下降沿都触发计数,所以每转一格计数器就会+4/-4。可以将PSC的值改为4-1,就是原脉冲数 配置定时器两个GPIO引脚模式,全部改成Pull-Up,即上拉模式,用于没有外部上拉的编码器读取时,可以确定引脚电平,防止出错。 业务代码: 主函数初始化代码,两个通道都要使能 HAL_TIM_Encoder_Start(&htim2,TIM_CHANNEL_1); HAL_TIM_Encoder_Start(&htim2,TIM_CHANNEL_2);测速 //__HAL_TIM_IS_TIM_COUNTING_DOWN(htim); //读取电机转动方向,用不到 //测速函数,short强制转换用于转换负值 int encoder_get(TIM_HandleTypeDef *htim) { int sp=0; sp =(short)__HAL_TIM_GET_COUNTER(htim); __HAL_TIM_SET_COUNTER(htim,0); return sp; } //定时器中断50ms调用 tim_it_50ms() { speed = encoder_get(&htim2);//视情况加正负号 }完成! ADC单通道、多通道 轮询、中断、DMA 轮询方式是阻塞式采集 开启连续模式(Continuous Conversion Mode)后,每次转换完成要调用HAL_ADC_Stop来关闭ADC,好处是可以开启一次ADC,进行多次重复采集。 例如,如果是单通道,非连续模式在完成一次ADC转换后就停止了,而连续模式会一直转换,如果是多通道AN1 ,AN2,AN3,单次转换模式会在三个通道转换完一次后停止,而连续转换模式在转换完AN3后,会从新再次去转换。 单通道轮询√业务代码 void ADC_Get() { HAL_ADC_Start(&hadc1); if(HAL_ADC_PollForConversion(&hadc1,50)==HAL_OK)//等待转换完成,第二个参数表示超时时间,单位ms { ADC_Value = HAL_ADC_GetValue(&hadc1); ADC_Volt = ADC_Value * 3.3 / 4096.0; } HAL_ADC_Stop(&hadc1);//若关闭连续模式,可去掉此句 } 多通道轮询√多个通道时必须开启间断模式(Discontinuous Conversion Mode),并且每个间断组中只有一个通道,否则每次只能读取到每组最后一个通道的值。后面还需设置设置通道转换顺序。 在多通道里面,第一此启用一次ADC的转换是采的第一个通道的(这里第一个通道是channel0);然后是第二个通道(channel1);第三次采又回到了第一个通道(channel0),依次类推。HAL_ADC_Start必须放在for循环中,否则只能采集第一个通道的ADC值。 uint16_t adc1_value[30]; //ADC多通道读取函数,num为通道数,阻塞式读取 void ADC_Get(uint8_t num) { int i=0; for(i=0;i int k=0; do { HAL_UART_Transmit(&huart1,(uint8_t * )(str+k),1,0xffff); k++; } while(*(str+k)!='\0'); return; }注意发送函数的字节数一定要核实!过大可能导致修改其他地址的数据!!! 或者重新定义printf函数来发送: //包含头文件 #include //在 USER CODE BEGIN 0 添加以下代码 //函数功能: 重定向c库函数printf到DEBUG_USARTx int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff); return ch; } //函数功能: 重定向c库函数getchar,scanf到DEBUG_USARTx int fgetc(FILE *f) { uint8_t ch = 0; while (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) == RESET);//等待串口输入数据 HAL_UART_Receive(&huart1, &ch, 1, 0xffff); return ch; }在keil勾选微库选项(必须勾选,但可能导致运行速度变慢) 然后就可以使用Printf函数和scanf,getchar函数 串口接收中断函数流程: HAL_UART_Receive_IT(中断接收函数) -> USART2_IRQHandler(void)(中断服务函数) -> HAL_UART_IRQHandler(UART_HandleTypeDef *huart)(中断处理函数) -> UART_Receive_IT(UART_HandleTypeDef *huart) (接收函数) -> HAL_UART_RxCpltCallback(huart);(中断回调函数) 1.添加定义 //添加头文件 #include //添加定义 #define RXSTRSIZE 256 //最大接收字节数 uint8_t rx_string[RXSTRSIZE]; //接收字符串数组 uint8_t rx_cnt=0; //接收字符串计数 uint8_t rx_buff; //接收缓存2.在主函数初始化处,调用一次接收中断函数 //中断接收1个字符,存储到rx_buff中 HAL_UART_Receive_IT(&huart1, (uint8_t *)&rx_buff, 1); //开启接收中断3.重写中断回调函数 回调函数中要调用一次HAL_UART_Receive_IT函数,使得程序可以重新触发接收中断 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart -> Instance == huart1.Instance) { //以下是串口接收中断业务代码 if(rx_cnt >= RXSTRSIZE - 1) //溢出判断 { rx_cnt = 0; memset(rx_string,0x00,sizeof(rx_string)); HAL_UART_Transmit(&huart1, (uint8_t *)"overfull!", 10,0xFFFF); } else { rx_string[rx_cnt++] = rx_buff; //接收数据转存 if(rx_string[rx_cnt-1] == 0x0A || rx_string[rx_cnt-2] == 0x0D) //判断结束位,0x0D是回车/r,0x0A是换行\n { HAL_UART_Transmit(&huart1, (uint8_t *)&rx_string, rx_cnt,0xFFFF); //将收到的信息发送出去 while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX); //判断发送是否完毕 memset(rx_string,0x00,sizeof(rx_string)); //清空接收字符串 rx_cnt = 0; //清空计数器 } } HAL_UART_Receive_IT(&huart1, (uint8_t *)&rx_buff, 1); //再开启接收中断,若去掉只能接收一次 } }完成! 附ascii码
|
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |