donatello1996 发表于 2020-8-10 09:08

【STM32F769Discovery开发板试用】串口空闲中断不定长接收&PWM输出方波

本帖最后由 donatello1996 于 2020-8-10 12:01 编辑

<p>&nbsp; &nbsp; &nbsp; &nbsp;串口空闲中断和PWM方波输出都是实际产品项目中非常常用的功能,串口空闲中断的好处在于可以使用不轮询方式,不占用CPU资源的前提下进行不定长串口字符串接收,因为触发方式是使用中断。使用空闲中断实现不定长接收的方式非常简单,只需要两点,一个是开启空闲中断,一个是开启接收DMA,这边我使用开发板的Arduino接口上面的串口6(USART6)来进行:</p>

<p></p>

<p>接收DMA初始化使用CubeMX自动一键生成:</p>

<p></p>

<pre>
<code>UART_HandleTypeDef huart6;
DMA_HandleTypeDef hdma_usart6_rx;

void UART6_Init(int baud)
{
        __HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_USART6_CLK_ENABLE();
        __HAL_RCC_DMA2_CLK_ENABLE();
       
GPIO_InitTypeDef GPIO_InitStruct;
       
        GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF8_USART6;
HAL_GPIO_Init(GPIOC, &amp;GPIO_InitStruct);
       
huart6.Instance = USART6;
huart6.Init.BaudRate = baud;
huart6.Init.WordLength = UART_WORDLENGTH_8B;
huart6.Init.StopBits = UART_STOPBITS_1;
huart6.Init.Parity = UART_PARITY_NONE;
huart6.Init.Mode = UART_MODE_TX_RX;
huart6.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart6.Init.OverSampling = UART_OVERSAMPLING_16;
huart6.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart6.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
HAL_UART_Init(&amp;huart6);

        hdma_usart6_rx.Instance = DMA2_Stream1;
        hdma_usart6_rx.Init.Channel = DMA_CHANNEL_5;
        hdma_usart6_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
        hdma_usart6_rx.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_usart6_rx.Init.MemInc = DMA_MINC_ENABLE;
        hdma_usart6_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
        hdma_usart6_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
        hdma_usart6_rx.Init.Mode = DMA_CIRCULAR;
        hdma_usart6_rx.Init.Priority = DMA_PRIORITY_LOW;
        hdma_usart6_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
        HAL_DMA_Init(&amp;hdma_usart6_rx);

        __HAL_LINKDMA(&amp;huart6,hdmarx,hdma_usart6_rx);
        __HAL_UART_ENABLE_IT(&amp;huart6,UART_IT_IDLE);
       
        HAL_NVIC_SetPriority(DMA2_Stream1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn);
       
        HAL_NVIC_SetPriority(USART6_IRQn, 0, 0);
        HAL_NVIC_EnableIRQ(USART6_IRQn);
}</code></pre>

<pre>
<code>void USART6_IRQHandler()
{
        int temp;
        if(__HAL_UART_GET_FLAG(&amp;huart6, UART_FLAG_IDLE))
        {
                __HAL_UART_CLEAR_IDLEFLAG(&amp;huart6);
                HAL_UART_DMAStop(&amp;huart6);
                temp=__HAL_DMA_GET_COUNTER(&amp;hdma_usart6_rx);
                rx6_len=BUFFERSIZE-temp;
                uart6_recv_end_flag=1;
        }
}</code></pre>

<p>主循环中使用轮询处理代码处理空闲中断中接收到的数据,这里是只有触发了空闲中断才会处理数据并重新开启DMA,所以是不会占用CPU轮询资源的:</p>

<pre>
<code>#define BUFFERSIZE 255
unsigned char rx6_buf,rx6_len=0,uart6_recv_end_flag;

void UART6_DMA_Get()
{
        if(uart6_recv_end_flag==1)
        {       
                uart6_recv_end_flag=0;
                printf("%s %d\n",rx6_buf,rx6_len);
                HAL_UART_Receive_DMA(&amp;huart6, (unsigned char*)rx6_buf, BUFFERSIZE);
        }
}
</code></pre>

<p>将串口6接入一个定时打印数值的传感器,就可以采集数据了,不过值得一提的是,串口空闲中断本质上仍是中断,如果空闲中断触发次数过于频繁,会严重影响CPU正常轮询工作,所以选用的串口传感器不能是每一帧发送间隔太短,频繁触发空闲中断的传感器。</p>

<p>然后是PWM,这里我使用Arduino接口的PF6即TIM10的通道1,可以直接参考原子代码来修改进行初始化:</p>

<p></p>

<pre>
<code>TIM_HandleTypeDef TIM10_Handler;
TIM_OC_InitTypeDef TIM10_CH1Handler;

void TIM10_PWM_Init(int arr,int psc)
{
        __HAL_RCC_TIM10_CLK_ENABLE();
        __HAL_RCC_GPIOF_CLK_ENABLE();
       
        GPIO_InitTypeDef GPIO_Initure;

        GPIO_Initure.Pin=GPIO_PIN_6;
        GPIO_Initure.Mode=GPIO_MODE_AF_PP;
        GPIO_Initure.Pull=GPIO_PULLUP;
        GPIO_Initure.Speed=GPIO_SPEED_HIGH;
        GPIO_Initure.Alternate=GPIO_AF3_TIM10;
        HAL_GPIO_Init(GPIOF,&amp;GPIO_Initure);
       
        TIM10_Handler.Instance=TIM10;
        TIM10_Handler.Init.Prescaler=psc;
        TIM10_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;
        TIM10_Handler.Init.Period=arr;
        TIM10_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
        HAL_TIM_PWM_Init(&amp;TIM10_Handler);

        TIM10_CH1Handler.OCMode=TIM_OCMODE_PWM1;
        //模式选择PWM1
        TIM10_CH1Handler.Pulse=arr/2;
        //设置比较值,此值用来确定占空比,
        //默认比较值为自动重装载值的一半,即占空比为50%
        TIM10_CH1Handler.OCPolarity=TIM_OCPOLARITY_LOW;
        HAL_TIM_PWM_ConfigChannel(&amp;TIM10_Handler,&amp;TIM10_CH1Handler,TIM_CHANNEL_1);
        HAL_TIM_PWM_Start(&amp;TIM10_Handler,TIM_CHANNEL_1);
}

void TIM_SetTIM10Compare1(int compare)
{
        TIM10-&gt;CCR1=compare;
}</code></pre>

<p>输出效果:</p>

<p></p>

<p>这里可以提一下TIM10的两个初始化参数,一个是重装载值,一个是分频值,重装载值的作用是设置一个PWM比较阈值,比如256,在0~256范围内占空比是0%~100%变化,而分频值就是PWM输出方波的频率,分频值越小,PWM频率越高,输出一个方波周期的时间就越短,这里我设置TIM10分频值为1,PWM波的输出间隔达到了1us级别,即1MHz,不过这样仍然算是频率较低的,之后我再继续探讨看看怎么输出更高频率的方波。</p>

<p>其它参数中,向上计数模式即为增量模式,即TIM10的计数器越计越多,模式为PWM1即占空比数值越大,一个周期内的低电平时间越长。</p>

donatello1996 发表于 2020-8-10 13:33

<p>初步推测,能输出更高频率PWM方波跟定时器本身有关系,试下换TIM2 TIM3这些更常用的定时器,看看能能不能输出更高频率的方波,做这个PWM实验的一大难题是开发板引出的GPIO引脚极其稀少,大部分都被FMC和LTDC两个接口的外设占用掉了,Arduino接口上引出的那些引脚每一个都是非常宝贵的资源。</p>

freebsder 发表于 2020-8-10 18:59

<p>你这是想搞到100分啊?</p>

donatello1996 发表于 2020-8-11 13:40

freebsder 发表于 2020-8-10 18:59
你这是想搞到100分啊?

<p>哈哈道长见笑了,反正这段时间都有在玩这板,写一下心得罢了</p>

freebsder 发表于 2020-8-11 20:54

donatello1996 发表于 2020-8-11 13:40
哈哈道长见笑了,反正这段时间都有在玩这板,写一下心得罢了

<p>挺好啊,写的很详细,幸好我转手了,要不和你比就相形见绌了。</p>

donatello1996 发表于 2020-8-11 21:27

freebsder 发表于 2020-8-11 20:54
挺好啊,写的很详细,幸好我转手了,要不和你比就相形见绌了。

<p>谢谢道长,道长过奖了</p>
页: [1]
查看完整版本: 【STM32F769Discovery开发板试用】串口空闲中断不定长接收&PWM输出方波