M-Arch(2)第一个示例:串口和CRC

本文详细介绍了在嵌入式系统中如何配置硬件资源,包括时钟分频、NVIC中断配置、系统定时器以及串口和硬件CRC模块的初始化。通过实例展示了如何利用STM32和GD32的硬件CRC计算CRC值,并通过串口将结果打印到电脑上,同时提供了STM和非STM平台的代码实现。此外,还讲解了如何支持printf功能,以便于调试输出。
摘要由CSDN通过智能技术生成

先把背景交代一下,上一篇讲了下基本的设计意图和框架,实际上整体框架已经有了,这篇先把整个框架再描述下,再来实现我们的第一个例子。

框架补遗

我把文件目录用 tree /F /A 命令打出来了如下:

  • app和boot:业务代码,其中timer0_task是定时器任务。

  • common:通用代码,一些通用接口,虚拟定时器这些可以放在这里。

  • firmwares:厂家提供的硬件库都放到这个文件夹下面,目前手头有2块开发板,一块是STM32的F103ZET6,一块是GD32的F450ZKT6,所以这里的库是这俩。

  • io:前面介绍过了,这里存放的是io封装层的代码。

+---app
|   |   main.c
|   |   timer0_task.c
|
+---boot
|       main.c
|
+---common
|   |   virtual_timer.c
|
+---firmwares
|   +---GD32F4xx_Firmware_Library
|   |   +
|   |   | ··· ···
|   |
|   \---STM32F10x_Firmware_Library
|   |   +
|   |   | ··· ···
|
|
\---io
    |   io.c
    |
    +---gd32
    |       interrupt.c
    |       io_crc.c
    |       io_gd32.c
    |       io_gd32.h
    |       io_gpio.c
    |       io_system.c
    |       io_timer.c
    |       io_uart.c
    |
    +---include
    |       io.h
    |       io_crc.h
    |       io_gpio.h
    |       io_system.h
    |       io_timer.h
    |       io_uart.h
    |
    \---stm32
            interrupt.c
            io_crc.c
            io_gpio.c
            io_stm32.c
            io_stm32.h
            io_system.c
            io_timer.c
            io_uart.c

由于本人比较认可兆易创新的封装方式,后续的代码封装尽量往GD上面靠;资源的命名按照常规的方式,串口1串口2这样的,而不是串口0串口1这样的。

开发板照片(红色箭头处是串口):

回到正题

我们先来做一个稍微比较不简单的例子:硬件计算CRC值并通过串口打印到电脑上。

先盘点下去需要的资源:

  • 内核相关的:包括时钟分频,nvic分组,系统tick等等

  • 虚拟OS:包括系统定时器

  • 资源相关的:硬件CRC模块,串口模块

内核相关 - 时钟分频

按默认,具体的去参见system_型号.c,就不细说了。

内核相关 - 系统tick

通用的,来自Core_m标准。

void system_tick_config(void)
{
    if (SysTick_Config(SystemCoreClock / SYSTEM_TICK_COUNT)){
        /* capture error */
        while (1){
        }
    }
    /* configure the systick handler priority */
    NVIC_SetPriority(SysTick_IRQn, 0x00U);
}

void delay_us(uint32_t count)
{
    delay = count;
    while(0U != delay){
    }
}

void delay_ms(uint32_t count)
{
    delay = 1000*count;
    while(0U != delay){
    }
}

void delay_decrement(void)
{
    if (0U != delay){
        delay--;
    }
}

内核相关 - nvic优先级

通用的,来自Core_m标准。

void nvic_priority_group_0_4(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
}

void nvic_priority_group_1_3(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
}

void nvic_priority_group_2_2(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
}

void nvic_priority_group_3_1(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);
}

void nvic_priority_group_4_0(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
}

针对STM派,封装NVIC中断配置:

void nvic_irq_enable(uint8_t channel, uint8_t pre_priority, uint8_t sub_priority)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = channel;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = pre_priority;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = sub_priority;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

系统定时器 - 驱动虚拟OS

采用定时器2(STM中是TIM2,GD中是TIMER1,-_-),需要进行不同的封装。

这里可以比对下代码,其他资源的操作跟这里差不太多。

这里再多嘴一句系统资源软件开发的基本步骤就三步:初始化时钟 - 配置使能 - 中断。

针对STM派,代码如下:


#include "io.h"
#include "io_stm32.h"

static void timerx_init(TIM_TypeDef* TIMx, uint16_t prescaler, uint16_t period);
static void timerx_init_full(TIM_TypeDef* TIMx, uint16_t prescaler, uint16_t period, uint16_t clock_div, uint16_t counter_mode, uint8_t repetition_counter);

void timer2_init(void)
{
    TIM_DeInit(TIM2);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    timerx_init(TIM2, 7199, 9);  // 10KHz 1ms
    nvic_irq_enable(TIM2_IRQn, 2, 0);
}

/******************************************************************************************/
/******************************************************************************************/
/******************************************************************************************/
/******************************************************************************************/
static void timerx_init(TIM_TypeDef* TIMx, uint16_t prescaler, uint16_t period)
{
    timerx_init_full(TIMx, prescaler, period, TIM_CKD_DIV1, TIM_CounterMode_Up, 0);
}

static void timerx_init_full(TIM_TypeDef* TIMx, uint16_t prescaler, uint16_t period, uint16_t clock_div, uint16_t counter_mode, uint8_t repetition_counter)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
    TIM_TimeBaseStructure.TIM_Period = prescaler;
    TIM_TimeBaseStructure.TIM_Prescaler = period;
    TIM_TimeBaseStructure.TIM_ClockDivision = clock_div;
    TIM_TimeBaseStructure.TIM_CounterMode = counter_mode;
    TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure);

    TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE );
    TIM_Cmd(TIMx, ENABLE);
}

针对非STM派:


#include "io.h"
#include "io_gd32.h"

static void timerx_init(uint32_t TIMx, uint16_t prescaler, uint16_t period);
static void timerx_init_full(uint32_t TIMx, uint16_t prescaler, uint16_t period, uint16_t clock_div, uint16_t counter_mode, uint8_t repetition_counter);

void timer2_init(void)
{
    timer_deinit(TIMER1);
    rcu_periph_clock_enable(RCU_TIMER1);
    timerx_init(TIMER1, 9999, 9);  // 10KHz 1ms
    timer_interrupt_enable(TIMER1, TIMER_INT_UP);
    nvic_irq_enable(TIMER1_IRQn, 1, 2);
}

/******************************************************************************************/
/******************************************************************************************/
/******************************************************************************************/
/******************************************************************************************/
static void timerx_init(uint32_t TIMx, uint16_t prescaler, uint16_t period)
{
    timerx_init_full(TIMx, prescaler, period, TIMER_CKDIV_DIV1, TIMER_COUNTER_UP, 0);
}

static void timerx_init_full(uint32_t TIMx, uint16_t prescaler, uint16_t period, uint16_t clock_div, uint16_t counter_mode, uint8_t repetition_counter)
{
    timer_parameter_struct timer_initpara;
    timer_initpara.prescaler         = prescaler;
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = counter_mode;
    timer_initpara.period            = period;
    timer_initpara.clockdivision     = clock_div;
    timer_initpara.repetitioncounter = repetition_counter;
    timer_init(TIMx, &timer_initpara);
    
    timer_update_event_enable(TIMx);
    timer_enable(TIMx);
}

资源 - 串口模块(打印)

采用串口1(STM中是USART1,GD中是USART0,-- --)。

串口的初始化没啥好说的,直接给代码吧。

STM派:

#include "io.h"
#include "io_stm32.h"
#include "io_uart.h"

void uart1_init(uint32_t baudrate)
{
    USART_InitTypeDef USART_InitStructure;
    USART_TypeDef* USART = USART1;

    USART_DeInit(USART);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    gpio_init(GPIOA, GPIO_Mode_AF_PP, GPIO_Speed_50MHz, GPIO_Pin_9);  ///<  USART1_TX   PA.9
    gpio_init(GPIOA, GPIO_Mode_IN_FLOATING, GPIO_Speed_50MHz, GPIO_Pin_10);  ///<  USART1_RX   PA.10

    USART_StructInit(&USART_InitStructure);
    USART_InitStructure.USART_BaudRate = baudrate;  ///<  波特率
    USART_Init(USART, &USART_InitStructure);
    USART_ITConfig(USART, USART_IT_RXNE, ENABLE);  ///<  使能中断
    USART_Cmd(USART, ENABLE);  ///<  使能串口

    nvic_irq_enable(USART1_IRQn, 3, 3);    
}

非STM派:

#include "io.h"
#include "io_gd32.h"
#include "io_uart.h"

void uart1_init(uint32_t baudrate)
{
    uint32_t com = USART0;

    usart_deinit(com);
    rcu_periph_clock_enable(RCU_USART0);
    rcu_periph_clock_enable(RCU_GPIOA);

    ///<  USART1_TX   PA.9
    gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_9);
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
    
    ///<  USART1_RX   PA.10
    gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_10);
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_10);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_10);   
    
    usart_baudrate_set(com, baudrate);
    usart_word_length_set(com, USART_WL_8BIT);
    usart_stop_bit_set(com, USART_STB_1BIT);
    usart_parity_config(com, USART_PM_NONE);
    usart_hardware_flow_rts_config(com, USART_RTS_DISABLE);
    usart_hardware_flow_cts_config(com, USART_CTS_DISABLE);
    usart_receive_config(com, USART_RECEIVE_ENABLE);
    usart_transmit_config(com, USART_TRANSMIT_ENABLE);
    usart_enable(com);

    nvic_irq_enable(USART0_IRQn, 3, 3);    
}

需要特别说明的是:如果需要串口支持printf,需要重新实现fputc函数并关闭半主机模式(semihosting),代码如下:

STM派:

#ifdef USING_PRINTF

#define PRINTF_UART USART1

#pragma import(__use_no_semihosting)               
//标准库需要的支持函数                   
struct __FILE   
{   
    int handle;   
    /* Whatever you require here. If the only file you are using is */   
    /* standard output using printf() for debugging, no file handling */   
    /* is required. */   
};   
/* FILE is typedef’ d in stdio.h. */   
FILE __stdout;

void _sys_exit(int x)
{   
    x = x;
}   

/* retarget the C library printf function to the USART */
int fputc(int ch, FILE *f)
{
    USART_SendData(PRINTF_UART, (uint8_t)ch );
    while(USART_GetFlagStatus(PRINTF_UART, USART_FLAG_TXE)==RESET);
    return ch;
}
#endif

非STM派:

#ifdef USING_PRINTF

#define PRINTF_UART USART0

#pragma import(__use_no_semihosting)               
//标准库需要的支持函数                   
struct __FILE   
{   
    int handle;   
    /* Whatever you require here. If the only file you are using is */   
    /* standard output using printf() for debugging, no file handling */   
    /* is required. */   
};   
/* FILE is typedef’ d in stdio.h. */   
FILE __stdout;

void _sys_exit(int x)
{   
    x = x;
}   

/* retarget the C library printf function to the USART */
int fputc(int ch, FILE *f)
{
    usart_data_transmit(PRINTF_UART, (uint8_t)ch);
    while(RESET == usart_flag_get(PRINTF_UART, USART_FLAG_TBE));
    return ch;
}

#endif

资源 - 硬件CRC模块

硬件CRC的使用比较简单,具体的可以读一下参考手册;把数传进去,然后读结果,读完结果之后记得清寄存器。

硬件CRC是CRC32,标准上是:CRC32/MPEG-2,多项式是:x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1,即:0x04C11DB7,初始值是:0xFFFFFFFF,输入数据反转:为false,输出数据反转:为false,结果异或值是:0x00000000。

关于CRC,可以参考我的另一篇文章:史上解释CRC最清楚的文章

需要把STM的接口进行封装:

#ifndef __IO_CRC_H__
#define __IO_CRC_H__

#ifdef STM32
    #define crc_deinit CRC_ResetDR
    #define crc_data_register_reset CRC_ResetDR
    #define crc_single_data_calculate CRC_CalcCRC
    #define crc_block_data_calculate CRC_CalcBlockCRC
    #define crc_free_data_register_write CRC_SetIDRegister
    #define crc_free_data_register_read CRC_GetIDRegister
#endif

void crc_init(void);

#endif /* __IO_CRC_H__ */

CRC测试代码:

static void crc_test(void)
{
    uint32_t data = 0xabcd1234;
    uint32_t crc_data = crc_single_data_calculate(data);
    printf("crc32 of 0x%X = 0x%X\r\n", data, crc_data);
    crc_data_register_reset();
}

结果演示

STM32F1-COM3,GD32F4-COM9

--EOF--

放个码维二,求粉,谢谢!

求粉
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值