首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >M-Arch(2)第一个示例:串口和CRC

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

作者头像
滚神大人
发布2021-09-10 15:12:23
发布2021-09-10 15:12:23
47500
代码可运行
举报
文章被收录于专栏:趣Python趣Python
运行总次数:0
代码可运行

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

框架补遗

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

  • app和boot:业务代码,其中timer0_task是定时器任务。
  • common:通用代码,一些通用接口,虚拟定时器这些可以放在这里。
  • firmwares:厂家提供的硬件库都放到这个文件夹下面,目前手头有2块开发板,一块是STM32的F103ZET6,一块是GD32的F450ZKT6,所以这里的库是这俩。
  • io:前面介绍过了,这里存放的是io封装层的代码。
代码语言:javascript
代码运行次数:0
运行
复制
+---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标准。

代码语言:javascript
代码运行次数:0
运行
复制
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标准。

代码语言:javascript
代码运行次数:0
运行
复制
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中断配置:

代码语言:javascript
代码运行次数:0
运行
复制
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派,代码如下:

代码语言:javascript
代码运行次数:0
运行
复制

#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派:

代码语言:javascript
代码运行次数:0
运行
复制

#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, 7199, 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派:

代码语言:javascript
代码运行次数:0
运行
复制
#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派:

代码语言:javascript
代码运行次数:0
运行
复制
#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派:

代码语言:javascript
代码运行次数:0
运行
复制
#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派:

代码语言:javascript
代码运行次数:0
运行
复制
#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的接口进行封装:

代码语言:javascript
代码运行次数:0
运行
复制
#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测试代码:

代码语言:javascript
代码运行次数:0
运行
复制
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--

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-09-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 趣Python 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 框架补遗
  • 回到正题
    • 内核相关 - 时钟分频
    • 内核相关 - 系统tick
    • 内核相关 - nvic优先级
    • 系统定时器 - 驱动虚拟OS
    • 资源 - 串口模块(打印)
    • 资源 - 硬件CRC模块
    • 结果演示
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档