• 欢迎访问吴爸爸的技术小木屋

c语言优秀的代码形式

C/C++ [email protected] 5年前 (2020-06-10) 1450次浏览 0个评论

1. 列表选择的模式

const unsigned int CAN_baud_table[CAN_BAUD_NUM][5] = 
{
    //波特率, CAN_SJW,   CAN_BS1,    CAN_BS2,CAN_Prescaler 
    {5,   CAN_SJW_1tq,CAN_BS1_13tq,CAN_BS2_2tq,450},              //未通          
    {10,  CAN_SJW_1tq,CAN_BS1_6tq,CAN_BS2_2tq, 400},              //未通          
    {15,  CAN_SJW_1tq,CAN_BS1_13tq,CAN_BS2_2tq,150},              //15K
    {20,  CAN_SJW_1tq,CAN_BS1_6tq, CAN_BS2_2tq,200},              //20k
    {25,  CAN_SJW_1tq,CAN_BS1_13tq,CAN_BS2_2tq,90},               //25k
    {40,  CAN_SJW_1tq,CAN_BS1_6tq, CAN_BS2_2tq,100},              //40k
    {50,  CAN_SJW_1tq,CAN_BS1_13tq,CAN_BS2_2tq,45},               //50k
    {62,  CAN_SJW_1tq,CAN_BS1_13tq,CAN_BS2_2tq,36},               //62.5k
    {80,  CAN_SJW_1tq,CAN_BS1_6tq, CAN_BS2_2tq,50},               //80k
    {100, CAN_SJW_1tq,CAN_BS1_5tq, CAN_BS2_2tq,45},               //100K
    {125, CAN_SJW_1tq,CAN_BS1_13tq, CAN_BS2_2tq,18},              //125K
    {200, CAN_SJW_1tq,CAN_BS1_6tq, CAN_BS2_2tq,20},               //200K
    {250, CAN_SJW_1tq,CAN_BS1_13tq,CAN_BS2_2tq,9},                //250k
    {400, CAN_SJW_1tq,CAN_BS1_6tq, CAN_BS2_2tq,10},               //400K
    {500, CAN_SJW_1tq,CAN_BS1_5tq, CAN_BS2_2tq,9},                //500K
    {666, CAN_SJW_1tq,CAN_BS1_5tq, CAN_BS2_2tq,8},                //未通
    {800, CAN_SJW_1tq,CAN_BS1_6tq, CAN_BS2_2tq,5},                //800K
    {1000,CAN_SJW_1tq,CAN_BS1_6tq, CAN_BS2_2tq,4},                //1000K
};

/***********************************************************************
文件名称:CAN_Baud_Process
功    能:计算波特率,返回
编写时间:2013.4.25
编 写 人:
注    意:
CAN_SJW : CAN_SJW_1tq - CAN_SJW_4tq   不能比任何一相位缓冲段长
CAN_BS1 : CAN_BS1_1tq - CAN_BS1_16tq
CAN_BS2 : CAN_BS2_1tq - CAN_BS2_8tq
CAN_Prescaler : 1 - 1024
    配置说明:
CAN_SJW + CAN_BS1 / (CAN_SJW + CAN_BS1 + CAN_BS2)
    0.75     baud > 800k
    0.80     baud > 500k
    0.875    baud <= 500k
    baud = 36 / (CAN_SJW + CAN_BS1 + CAN_BS2) / CAN_Prescaler
***********************************************************************/
void CAN_Baud_Process(unsigned int Baud,CAN_InitTypeDef *CAN_InitStructure)
{
    unsigned int i = 0;
    for(i = 0;i < CAN_BAUD_NUM;i ++)
    {
        if(Baud == CAN_baud_table[i][0])
        {
            CAN_InitStructure->CAN_SJW = CAN_baud_table[i][1];
            CAN_InitStructure->CAN_BS1 = CAN_baud_table[i][2];
            CAN_InitStructure->CAN_BS2 = CAN_baud_table[i][3];
            CAN_InitStructure->CAN_Prescaler = CAN_baud_table[i][4];
            break;  
        }
    }   
}

2. 宏定义取代函数的多参数的情况

  • 下面有a,b,c,d四个参数
void Tcp_Client_Init(uint16_t local_port,uint16_t server_port,unsigned char serverIpAddr_a,unsigned char serverIpAddr_b,unsigned char serverIpAddr_c,unsigned char serverIpAddr_d)
{
    struct tcp_pcb *tcp_client_pcb;
    struct ip_addr ipaddr;

    /* write target_server_ip to a structure*/
    IP4_ADDR(&ipaddr, serverIpAddr_a,serverIpAddr_b,serverIpAddr_c,serverIpAddr_d);

    /* assign a tcp_pcb structure to a tcp_client*/
    tcp_client_pcb = tcp_new();

    /* bind local ip and local port*/
    tcp_bind(tcp_client_pcb, IP_ADDR_ANY, my_local_port);
    //tcp_bind(tcp_client_pcb, IP_ADDR_BROADCAST, my_local_port);

    if (tcp_client_pcb != NULL)
    {
        /* connect to target_server, parameters include the target_server_port and the target_server_ip*/
        tcp_connect(tcp_client_pcb, &ipaddr, my_server_port, tcp_client_connected);
    }
}
  • 定义一个宏定义, 实际上是一个分散的, 因为宏定义的替代的作用
#define my_server_ipaddr 192,168,1,142
  • 在调用的地方,这里my_server_ipaddr就代表最初的a,b,c,d
Tcp_Client_Init(my_local_port,my_server_port,my_server_ipaddr);

3. 字符串遇到空格停止, 忽略任何字符串

scanf("%d%*s%d",&a,&b);就是说:
读取一个整型数,放到a里;忽略中间输入的任何串(假如存在);读取另一个整型数,放到b里。

4. c语言中的退格(删除)

backspace 光标左移1格。
如果有后继输出,就能覆盖掉一个字符,若无后继输出,不会用空白覆盖\b压着的字符。
例如:
char s1[10]="abcd\b";
char s2[10]="abcd\bD";
printf("s1: %s\n",s1); //输出 s1: abcd
printf("s2: %s\n",s2); //输出 s2: abcD

5. api的方式

  • 善于使用中间函数指针的方式(比如usart中中断接收, 但是中断接收的处理函数是谁不知道, 那么就可以用函数指针虚拟一个出来, 在正式使用的时候我们要把这个函数指针指向具体的函数)
  • 善于用宏定义(宏定义就是替换, 比如tty0_UART可以代替uart1, 这样在使用的地方修改一下宏定义就好了比如改为uart2)
  • 善于用高内聚低耦合, 就是内部的就是内部的, 外部的就是外部的, 不要混合用
  • 善于把结构分层, 这样就不会牵一发而动全身
typedef bool    (*pbFun_Bytex)(uint8_t * pcByte, uint16_t len );// 定义一个函数指针类型, 可以定义函数指针了
/*---------------------* 
*    TTYx 句柄结构
*----------------------*/
typedef struct TTYx_HANDLE_STRUCT 
{
    const char  * const name;       //驱动器名
    const uint16_t      rxSize;     //接收大小
    const uint16_t      txSize;     //发送大小

    //------------------------------------------------------
    //step1: 注入回调函数
    const pvFunWord     init;           //初始化.
    const pbFun_Bytex   api_TxdFrame;   //发送数据帧. (发送帧)
    const pbFunChar     api_TxdByte;    //发送数据字节

    //------------------------------------------------------
    //step2: 接收回调函数
    pbFun_Bytex         inj_RcvFrame;   //(ISR)接收数据帧. (接收帧)
    pvFunDummy          inj_TxdReady;   //(ISR)发送完毕回调

    //------------------------------------------------------
    //step3: 接收回调函数
    struct TTYx_HANDLE_STRUCT * pvNext; //连接到下一个指令 
}TTYx_HANDLE;
TTYx_HANDLE     apis_tty0 =
{
    .name           = "stm32_tty0",
    .rxSize         = TTY0_MAX_RX_BYTE,
    .txSize         = TTY0_MAX_TX_BYTE,
    .init           = &TTY0_Init,
    .api_TxdFrame   = &TTY0_Send,
    .api_TxdByte    = &TTY0_SendByte,
    .inj_RcvFrame   = NULL,
    .inj_TxdReady   = NULL,
    .pvNext         = NULL,
};

6. 善于利用宏定义

6.1 宏定义帮助调试(一句话搞定)

#include "stdio.h"
#include "stdint.h"
#include "stdbool.h"

#define CAN_DEBUG(...) (CAN_DEBUG_flag==true)?printf(__VA_ARGS__):CAN_DEBUG_flag

bool CAN_DEBUG_flag = true;

int main(void)
{
    uint8_t d = 8;
    CAN_DEBUG("printf it %d\r\n",d);
}

6.2 打印变量名字

#define name2str(name) (#name)

// 打印函数r
void variable_printf(char *var_name,int var_value)
{
    printf("%s = %d\r\n",var_name,var_value);
}

//调用, 其中有把变量名改为字符串 
variable_printf(name2str(my_struct.a),my_struct.a);

6.3 获得数组的大小

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

6.4 宏定义代替函数的参数(减少输入,减少出错)

#include "stdio.h"
#include "stdint.h"
#include "string.h"

uint8_t server_ip[4] = {0};
uint16_t server_port = 0;

static uint8_t my_ip[4] = {192,168,1,114};
static uint16_t my_port = 11553;

#define NEED2SET_IP my_ip[0],my_ip[1],my_ip[2],my_ip[3]
#define SERVER_IP  server_ip[0],server_ip[1],server_ip[2],server_ip[3]

void test(uint8_t ip_0,uint8_t ip_1,uint8_t ip_2,uint8_t ip_3,uint16_t port)
{
    server_ip[0] = ip_0;
    server_ip[1] = ip_1;
    server_ip[2] = ip_2;
    server_ip[3] = ip_3;
    server_port  = port;
    printf("%d,%d,%d,%d:%d\r\n",SERVER_IP,server_port);
}

int main(void)
{
    //不推荐的做法
    test(my_ip[0],my_ip[1],my_ip[2],my_ip[3],my_port);

    printf("the next\r\n");
    memset(server_ip,0x00,sizeof(server_ip));

    //推荐的做法
    test(NEED2SET_IP,my_port);
}

6.5 替代纯数字,让数字有意义

#define ONE_HUDRAND_mA 1000       // 代表1000mA
#define SIXTY_SECONDS  60         // 代表60s
#define PI             3.1415926  // 代表PI值

6.6 宏定义函数名(方便复用和配置)

#define usartx_IRQHandler USART1_IRQHandler //这样就可以随意改为自己想要的中断, 而函数只有一个不变

void usartx_IRQHandler(void)
{
    uint32_t rx_count = 0;

    if(USART_GetITStatus(USART_SNUM, USART_IT_IDLE) != RESET)
    {
        dma.DMA_Disable();
        rx_count = USART_SNUM->DR;                //清USART_IT_IDLE标志
        rx_count = USART_SNUM->SR;
        rx_count = dma.get_data_count();

//        My_printf("rx_count = %d\r\n",rx_count); // send rx
//        My_printf("%s",&DMA_RX_BUFF[0]);         // send what rx

        dma.DMA_Counter_clr();
        dma.DMA_Enable();
    }
}
  • 优点是配置的时候比较方便, 不用改c文件中函数, 只需要修改h文件

6.7 代替函数执行

  • 判断闰年
#include <cstdio>
#include <algorithm>
#include <cstring>
#define LEAP_YEAR(y) ((y%400==0)||(y%4==0&&y%100!=0))?'L':'N'
using namespace std;

int main()
{
    int y;
    scanf("%d",&y);
    printf("%c",LEAP_YEAR(y));
}
  • 比较大小
define MAX(A,B) A > B ? A : B

6.8 简化操作

  • 得到一个字的高低位
// 得到一个字的高位和低位
#define WORD_LO(xxx) ((byte) ((word)(xxx) & 255))

#define WORD_HI(xxx) ((byte) ((word)(xxx) >> 8))
  • 大写字母转为小写字母
//将字符从大写转化成小写
#define char_tolower(c)      (u_char) ((c >= 'A' && c <= 'Z') ? (c | 0x20) : c) 
//将字符从小写转化成大写
#define char_toupper(c)      (u_char) ((c >= 'a' && c <= 'z') ? (c & ~0x20) : c)

6.9 宏定义容易出错的地方(没有用括号)

要不要用括号, 不一定
如下:

  • 方式一
#define N 2+3
  • 方式二
#define N (2+3)

同样的N*N一个结果是2+3*2+3=11,另一个是(2+3)*(2+3)=25

宏定义参考地址
宏定义参考地址

7. 枚举类型作为数组的下标

typedef enum {PERIOD_T15m = 0,PERIOD_T24h,PER_PERIOD_NUM} PerPeriod;

//那么我们就可以使用枚举值来定义数组了:

unsigned char               trip_en[PER_PERIOD_NUM];

//同样引用数组元素方法如下:

trip_en[PERIOD_T15m],trip_en[PERIOD_T24h]
  • 推荐的方法
//这段代码有个潜在前提,那就是枚举的顺序不能变化。一旦变化,整个逻辑都乱了,这样的BUG能否查出来要靠运气。这样的假设是靠不住的,特别是当枚举是第三方库提供的时候,他们调整枚举的顺序后绝对不会通知你。为了避免这样的潜在隐患,可以用下面的方法:

Enum
{
    FRUIT_APPLE,
    FRUIT_PEAR,
    FRUIT_BANANA,
    FRUIT_NR
};

 int price[FRUIT_NR] = { [FRUIT_APPLE] = 20, [FRUIT_PEAR] = 25, [FRUIT_BANANA] = 30};
  • 更高级的用法, 比如这个有多个地方在应用, 但是比较混乱, 而且也不一致, 下面的发发就出现了同样的枚举, 但是意义表示不一样, 能够更加灵活的分配意义了, 好处就不用多次调用一个而名字又不一样的麻烦了
Enum
{
    FRUIT_APPLE,
    FRUIT_PEAR,
    FRUIT_BANANA,
    FRUIT_NR
};

const int price[FRUIT_NR] = { [FRUIT_APPLE] = 20, [FRUIT_PEAR] = 25, [FRUIT_BANANA] = 30};

const int my_price[FRUIT_NR] = { [FRUIT_APPLE] = 21, [FRUIT_PEAR] = 24, [FRUIT_BANANA] = 31};

8. 添加xx_info.h xx_info.c

  • 一个模块的整体的信息都在这个里面
  • 这个能够快速修改信息结构体和enum等等

9. 添加xx_config.h xx_config.h

  • 一个模块的所有的配置的信息
  • 这个能够快速修改配置

10. 头文件尽量单向调用

11. 清除buff(一定要用0x00, 因为很多库如果不行直接返回的是0x00)

memset(my_pop_buf,0x00,sizeof (my_pop_buf));

// 或者

char result[10] = {0};

12. 任何数值(包括指针和数组)都需要赋初值

13. 定义枚举类型最好的方式

typedef enum _charging_mode_
{
    lithium_battery     = 0x01,
    LeadAcid_battery,
    lithium_battery_diy
};

// 可以这样使用定义枚举类型
enum _charging_mode_ abc;

14. API函数(适合单片机)

  • 头文件
#ifndef __TEST_H
#define __TEST_H

#include <stdint.h>
#include <stdio.h>

// 这里面的东西就是对外函数的照抄, 但是注意要变成指针形式
typedef struct
{
    void (*my_printf)(uint8_t *buff);
    uint8_t (* get_value)(void);
}test_fun;

extern test_fun test;

#endif
  • c文件
#include "test.h"

void my_printf(uint8_t *buff)
{
    printf("%s",buff);
}

uint8_t get_value(void)
{
    return 5;
}

// api
test_fun test =
{
    .my_printf = &my_printf,
    .get_value = &get_value
};

int main(void)
{
    test.my_printf("12345678\r\n");
    printf("%d\r\n",test.get_value());
}

14.1. 使用方法

  1. 正常的写程序
  2. 写一个名为文件名_fun的结构体, 把对外函数都放到这个结构体中来,转化为指针
  3. 实体化这个文件名_fun的结构体, 并名为文件名, 把对应的结构体指针对应的外部函数名, 将这个名为文件名的结构体对外 extern, 以后只要调用这个extern就可以了

15. 让每个模块自己进行初始化, 这样就可以初始化函数作为内部函数了

#include "stdio.h"

typedef int (*MyFun)(void);

#define INIT_FUN(fn, level)\
const MyFun __myFun_##fn __attribute__((section(".myFun." level))) __attribute__((used)) = fn

static void  init_begin(void)
{

    printf("----fun init start---\r\n");
    return 0;
}

INIT_FUN(init_begin, "0");

static int init_end(void)

{
    printf("----fun init end---\r\n");
    return 0;
}
INIT_FUN(init_end, "7");

static void init_test(void)
{
    printf("init_test ok\r\n");
}

INIT_FUN(init_test, "0");

const MyFun *vMyFun;
void main(void)
{
    for (vMyFun = &__myFun_init_begin; vMyFun <= &__myFun_init_end; vMyFun++)
    {

        (*vMyFun)();
    }

    for (;;);
}

16. DEBUG printf(这样可以xcshell中进行控制flag从而达到具体的控制)

bool CAN1_DEBUG_flag     = false;
#define CAN1_DEBUG(...)     (CAN1_DEBUG_flag==true)?usart.My_printf(__VA_ARGS__):CAN1_DEBUG_flag

17. 位域操作(经验证)

#include "stdio.h"
#include "string.h"

union
{
    uint8_t whole;
    struct 
    {
        uint8_t bit0:1;
        uint8_t bit1:1;
        uint8_t bit2:1;
        uint8_t bit3:1;
        uint8_t bit4:1;
        uint8_t bit5:1;
        uint8_t bit6:1;
        uint8_t bit7:1;
    }byte;
}bittest[2];

// 这里是替代的作用, 避免写的冗长
#define a bittest[0].byte.bit0
#define b bittest[0].byte.bit1
// ....

#define c bittest[1].byte.bit0
#define d bittest[1].byte.bit1
// ....

int main()
{
    a = 1;
    b = 1;

    c = 0;
    d = 1;
    printf("%d\r\n",bittest[0].whole);
    printf("%d\r\n",bittest[1].whole);
    return 0;
}
  • 实践中的改动

    typedef union
    {
      uint8_t STATUS;
      struct
      {
          uint8_t standby_bit0:1;
          uint8_t hardwareError_bit1:1;
          uint8_t charging_bit2:1;
          uint8_t fullyCharged_bit3:1;
          uint8_t lithiumBattery_bit4:1;
          uint8_t LeadAcidBattery_bit5:1;
          uint8_t lithiumBatteryDiy_bit6:1;
      }STATUS_byte;
    }charger_status_union;

18. 多个软定时器

  • timer.h
#ifndef __TIMER_H
#define __TIMER_H

#include "sys.h"
#include "charger_config.h"
#include "led.h"

#define RELOADTIME 20 // 20*0.5s = 10s

#define TIME_END 1
#define TIME_ENOUGH 0

typedef struct
{
    uint8_t flag;
    uint8_t reload_time;
}softTimer_struct;

typedef struct
{
    void (*TIM2_Init)(u16 arr, u16 psc);
    softTimer_struct *softTimer;

}timer_API;

extern timer_API timer;

#endif
  • timer.c
  #include "timer.h"

static softTimer_struct softTimer[charger_num];

static void TIM2_IRQ_Process(void);
static void softTimer_Init(void);
static void TIM2_Init(u16 arr, u16 psc);

static void TIM2_Init(u16 arr, u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    TIM_TimeBaseStructure.TIM_Period                     = arr;
    TIM_TimeBaseStructure.TIM_Prescaler                  = psc;
    TIM_TimeBaseStructure.TIM_ClockDivision              = 0;
    TIM_TimeBaseStructure.TIM_CounterMode                = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

    NVIC_InitStructure.NVIC_IRQChannel                   = TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
    TIM_Cmd(TIM2, ENABLE);

    softTimer_Init();
}

void TIM2_IRQHandler(void)
{
  if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
  {
    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    TIM2_IRQ_Process();
  }
}

static void softTimer_Init(void)
{
    uint8_t i = 0;
    for(i=0;i<charger_num;i++)
    {
        softTimer[i].flag = TIME_END;
        softTimer[i].reload_time = 0;
    }
}

static void TIM2_IRQ_Process(void)
{
    uint8_t i = 0;
    for(i=0;i<charger_num;i++)
    {
        if(softTimer[i].reload_time != 0)
        {
            softTimer[i].reload_time--;
        }
        else
        {
            my_charger[i].canCommunication_state = abnormal_state;
            softTimer[i].flag = TIME_END;
        }
    }
}

timer_API timer =
{
    .TIM2_Init = TIM2_Init,
    .softTimer = softTimer
};

19. 调度器

  • taskCore.h
#ifndef __TASKCORE_H
#define __TASKCORE_H

#include "sys.h"
#include "project_config.h"
#include "led.h"
#include "ds28e17.h"

#define TASK_NUM 10

typedef void (*TaskHook)(void);

typedef struct _TASK_COMPONENTS
{
    uint8_t  Run;
    uint16_t Timer;
    uint16_t ItvTime;
    TaskHook my_task;
}_WRS_PACK_ALIGN(1) TASK_COMPONENTS;

struct TaskNode
{
    struct _TASK_COMPONENTS this_task;
    struct TaskNode *next;
};

typedef struct
{
    void (*TaskInit)(void);
    void (*TaskProcess)(void);
}taskCore_api;

extern taskCore_api taskCore;

#endif
#include "taskCore.h"

static void TaskRemarks(void);

static struct TaskNode taskNode[TASK_NUM];

static void Task_TIM_Init( void )
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

    TIM_TimeBaseStructure.TIM_Period                     = 10-1;
    TIM_TimeBaseStructure.TIM_Prescaler                  = 7200-1;
    TIM_TimeBaseStructure.TIM_ClockDivision              = 0;
    TIM_TimeBaseStructure.TIM_CounterMode                = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

    NVIC_InitStructure.NVIC_IRQChannel                   = TIM3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

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

void TIM3_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
    {
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
        TaskRemarks();
    }
}

static uint8_t register_task_num = 0;
static void register_task(uint8_t Run,uint16_t Timer,uint16_t ItvTime, TaskHook this_taskHook)
{
    uint8_t i = 0;
    if(register_task_num == 0)
    {
        taskNode[i].this_task.Run       = Run;
        taskNode[i].this_task.Timer     = Timer;
        taskNode[i].this_task.ItvTime   = ItvTime;
        taskNode[i].this_task.my_task = this_taskHook;
        taskNode[i].next = NULL;
        register_task_num++;
    }
    else
    {
        for(i=0;i<register_task_num;i++)
        {
            if(taskNode[i].this_task.my_task == NULL)
            {
                return;
            }
        }
        if(i == register_task_num)
        {
            taskNode[i-1].next = &taskNode[i];
            taskNode[i].this_task.Run      = Run;
            taskNode[i].this_task.Timer    = Timer;
            taskNode[i].this_task.ItvTime  = ItvTime;
            taskNode[i].this_task.my_task  = this_taskHook;
            register_task_num++;
        }
    }
}

static void RegisterAllTask(void)
{
//    register_task(0,250,250,led.heartBeat_flashing);
    register_task(0,500,500,led.ledRed_flashing);
    register_task(0,100,100,ds28e17.Initialization);
}

static void TaskInit(void)
{
    RegisterAllTask();
    Task_TIM_Init();
}

static void TaskRemarks(void)
{
    uint8_t i;
    for (i = 0; i<register_task_num; i++)
    {
        if (taskNode[i].this_task.Timer)
        {
            taskNode[i].this_task.Timer--;
            if (taskNode[i].this_task.Timer == 0)
            {
                taskNode[i].this_task.Timer = taskNode[i].this_task.ItvTime;
                taskNode[i].this_task.Run = 1;
            }
        }
    }
}

void TaskProcess(void)
{
    uint8_t i;
    for (i = 0; i<register_task_num; i++)
    {
        if (taskNode[i].this_task.Run)
        {
            taskNode[i].this_task.my_task();
            taskNode[i].this_task.Run = 0;
        }
    }
}

taskCore_api taskCore =
{
    .TaskInit      = TaskInit     ,
    .TaskProcess   = TaskProcess
};
  • 使用方法
void board_Init()
{
    // new
//    I2C_DRV.I2C_Initializes();
    led.LED_DRV_Cfg();
    usart.usart_drv_cfg(115200);
    ds28e17.drv_cfg();
    taskCore.TaskInit();
    delay.delay_init();
}

int main(void)
{
    board_Init();
    delay.delay_ms(20);
    for(;;)
    {
        taskCore.TaskProcess();
    }
    return 0;
}

20. usart

  • usart.h
#ifndef __USART_H
#define __USART_H

#include "stm32f10x.h"
#include <stdio.h>
#include "project_config.h"

#define USART_REMAP
#define USART_REMAP_USARTX GPIO_Remap_USART1
#define USART_SNUM         USART1
#define USART_GPIO_PORT    GPIOB
#define USART_TX_GPIO_PIN  GPIO_Pin_6
#define USART_RX_GPIO_PIN  GPIO_Pin_7
#define USART_IRQ          USART1_IRQn

#define USART_GPIO_RCC    RCC_APB2Periph_GPIOB | RCC_APB2Periph_USART1 | RCC_APB2Periph_AFIO

typedef  struct
{
    void (*usart_drv_cfg)(uint32_t baud);
    void (*USART_X_SendNData)(u8 *str,u16 len);
    int  (*My_printf)(const char *format,...);
}usart_api;

extern usart_api usart;

#endif
  • usart.c
#include "usart.h"
#include <stdarg.h>

static void usart_drv_cfgRCC(void);
static void usart_drv_cfgGPIO(void);
static void usart_drv_cfgParamter(uint32_t baud);
static void usart_drv_cfgInterrupt(void);
static void usart_drv_cfgNVIC(void);

void usart_drv_cfg(uint32_t baud)
{
    usart_drv_cfgRCC();
    usart_drv_cfgGPIO();
    usart_drv_cfgParamter(baud);
    usart_drv_cfgInterrupt();
    usart_drv_cfgNVIC();
}

static void usart_drv_cfgRCC(void)
{
    RCC_APB2PeriphClockCmd(USART_GPIO_RCC,ENABLE);
}

static void usart_drv_cfgGPIO(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

#ifdef USART_REMAP
    GPIO_PinRemapConfig(USART_REMAP_USARTX,ENABLE);
#endif

    //tx
    GPIO_InitStructure.GPIO_Pin = USART_TX_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(USART_GPIO_PORT, &GPIO_InitStructure);

    // rx
    GPIO_InitStructure.GPIO_Pin = USART_RX_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(USART_GPIO_PORT, &GPIO_InitStructure);
}

static void usart_drv_cfgParamter(uint32_t baud)
{
    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate = baud;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No ;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART_SNUM, &USART_InitStructure);
}

static void usart_drv_cfgInterrupt(void)
{
    USART_ITConfig(USART_SNUM, USART_IT_RXNE, ENABLE);
    USART_Cmd(USART_SNUM, ENABLE);
    USART_ClearITPendingBit(USART_SNUM, USART_IT_TC);
}

static void usart_drv_cfgNVIC(void)
{
    NVIC_InitTypeDef   NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = USART_IRQ;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

static void USART_X_SendNData(u8 *str,u16 len)
{
    unsigned int i = 0;
    for(i = 0;i < len;i++)
    {
        USART_SendData(USART_SNUM,*str);
        while(USART_GetFlagStatus(USART_SNUM,USART_FLAG_TC)==0);
        str++;
    }
}

static void USART_X_SendData(u8 ch)
{
    USART_SendData(USART_SNUM,ch);
    while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==0);
}

static void USART_X_SendString(u8 *str)
{
    while(*str)
    {
        USART_SendData(USART_SNUM,*str);
        while(USART_GetFlagStatus(USART_SNUM,USART_FLAG_TC)==0);
        str++;
    }
}

static void Clear_Buff(u8 *str,u16 len)
{
    u8 i;
    for(i=0;i<len;i++)       *(str+i)=0;
}

int __io_putchar(int ch)
{
    USART_SendData(USART_SNUM,(unsigned char)ch);
    while(USART_GetFlagStatus(USART_SNUM,USART_FLAG_TC)==RESET);
    return ch;
}

static char stdbuff[512];
static int My_printf(const char *format,...)
{
    uint16_t n;
    va_list ap;
    va_start(ap,format);

    n=(uint16_t)vsprintf(stdbuff,format,ap);

    va_end(ap);

    USART_X_SendNData((uint8_t *)stdbuff,n);

    return n;
}

usart_api usart =
{
    .usart_drv_cfg     = usart_drv_cfg    ,
    .USART_X_SendNData = USART_X_SendNData,
    .My_printf         = My_printf        ,
};

21. 打印结构体的方法

#include "stdio.h"
#include "stdint.h"

// must be use
#define name2str(name) (#name)

typedef struct
{
    uint8_t  a;
    uint16_t b;
    uint32_t c;
}test_struct;

test_struct my_struct = 
{
    .a = 1,
    .b = 2,
    .c = 3
};

// must be use
void variable_printf(char *var_name,int var_value)
{
    printf("%s = %d\r\n",var_name,var_value);
}

int main(void)
{
    // example to be use
    variable_printf(name2str(my_struct.a),my_struct.a);
    variable_printf(name2str(my_struct.b),my_struct.b);
    variable_printf(name2str(my_struct.c),my_struct.c);
    return 0;
}

22. 变量定义时不能使用变量, 但是有一种是例外

#include "stdio.h"
#include "stdint.h"
#include "string.h"
#include "stdbool.h"

/*这种方法就可以*/
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
uint8_t my_frameHead[ARRAY_SIZE(dd)];

/*下面这种方式不可以, 不能编译过, 原因是变量定义时不能使用变量*/
// uint8_t a = 3;
// uint8_t my_frameHead1[a];

int main(void)
{
    printf("%d\r\n",ARRAY_SIZE(my_frameHead));
    return 0;
}

23. 搜索关键字的算法

使用条件: 仅适用于有包头的协议, 并且只适合一次只能取一个关键字, 但是要比较连续的几个字节

#include "stdio.h"
#include "stdint.h"
#include "string.h"
#include "stdbool.h"

/*
 *  思路:
 *  先读取对应长度的数据
 *  如果没有检测到包头, 数组往前推一个, 丢掉第一个数据, 最后一个数据从buffer中读出来, 以此类推
 */

uint8_t buffer_need2Search[] = {0x11, 0x12, 0x13, 0x11, 0x12, 0x13, 0x15,0x11, 0x12, 0x13, 0x14};
uint8_t pkgHead[]             = {0x11, 0x12, 0x13, 0x14};

/*这种方法就可以*/
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
uint8_t my_frameHead[ARRAY_SIZE(pkgHead)];

// 如果是读取ringbuffer, 这个data_count不需要
static uint8_t data_count = 0;

bool getSourceData(uint8_t *reg)
{
    if(data_count>=ARRAY_SIZE(buffer_need2Search)) return false;// 如果是ringbuffer, 那么可以根据获得值的返回值来确定是否获得数据
    *reg = buffer_need2Search[data_count++];
    return true;
}

void restart_search(void)
{
    data_count = 0;
}

bool getdata(void)
{
    uint8_t i = 0;
    uint8_t temp;
    if (data_count == 0)
    {
        for (i = 0; i < ARRAY_SIZE(my_frameHead); i++)
        {
            // if can not get ringbuffer data return false
            if (getSourceData(&temp) == false)
                return false;
            my_frameHead[i] = temp;
        }
    }
    else
    {
        for (i = 0; i < ARRAY_SIZE(my_frameHead) - 1; i++)
        {
            my_frameHead[i] = my_frameHead[i + 1];
        }
        // if can not get ringbuffer data return false
        if (getSourceData(&temp) == false)
            return false;
        my_frameHead[i] = temp;
    }
    return true;
}

bool search_premble(void)
{
    restart_search();
    while (getdata() == true)
    {
        if (memcmp(my_frameHead, pkgHead, 4) == 0)
            return true;
    }
    return false;
}

// how to use
int main(void)
{
    if (search_premble() == true)
    {
        printf("get premble\r\n");
    }
    else{
        printf("do not get premble\r\n");
    }
    return 0;
}

24. void *指针的妙用(也有函数指针的妙用, 作为api使用)

  • 这里使用的是函数指针, 函数指针的参数中有一个就是void *指针
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>

typedef struct
{
    uint8_t a;
    uint16_t b;
    uint32_t c;
}test_struct;

test_struct my_test_struct = 
{
    .a = 1,
    .b = 2,
    .c = 3
};

// 这个可以是ringbuffer的get_one_DataFromRingbuffer()函数
bool getData(uint8_t *reg, test_struct *my_struct)
{
    *reg = my_struct->b;
    return true;
}

/*定义一个函数指针, 注意后面的参数, 后面的buffer是一个void指针, 当参数可以是任意类型指针的时候, 就可以使用void*, 这样就不用在意类型转换了*/
bool (*getSourceData)(uint8_t *reg,void *buffer);

int main(void)
{
    uint8_t a = 0;
    getSourceData = (void *)getData; // 将函数指针指向具体的函数, 可以指向get_one_DataFromRingbuffer()函数, 这样就直接从ringbuffer中获取单个数据了, 前面加(void *)主要是函数指针和函数的指针类型是不一样的, 强制转换一下
    if(getSourceData(&a,&my_test_struct) == true)
    {
        printf("%d\r\n",a);
    } 
    return 0;
}

25. 宏定义放置位置的原则

  1. 如果外部不需要使用, 那么就定义在c文件中
  2. 如果外部需要使用, 那么就定义在h文件中

26. shell的方法

  1. 每一个shell独占一个c文件和一个h文件, 将h文件添加到总的shell中, 总的shell文件会使用api的方式进行调用
  2. 添加一个shell和删除一个shell直接在总的shell中执行, 总的shell会删除对应的头文件和链接点

27. uint32_t数据转为数组, 然后从数组还原回uin32_t数据(也适用于uint16_t)(不能用于ringbuffer的一个个的读取方式,需要上下换位)

#include "stdio.h"
#include "stdint.h"
#include "string.h"

uint8_t a[4] = {0};
uint32_t b = 0x12345678;
uint32_t datalen = 0;

void getPkgLen(void)
{
    uint8_t i = sizeof(datalen);
    do
    {
        datalen <<= 8;
        datalen |= a[i];
    } while (i--);
}

int main(void)
{
    memcpy(a, (uint8_t *)&b, sizeof(datalen));
    getPkgLen();
    printf("b = %x\r\n", b);
    printf("datalen = %x\r\n", datalen);
    return 0;
}

28. 定义16位的交换和32位的交换

#define SWAP_16(x) x=(uint16_t)(x>>8|x<<8)
#define SWAP_32(x) x=(x >> 24 | (x >> 8) & 0x0000ff00 | (x << 8) & 0x00ff0000 | x << 24)

29. 宏定义判断是否正确

#include "stdio.h"
#include "stdbool.h"
#include "stdint.h"

#define ISONE(x) (x==1 ? true:false)

int main(void)
{
    uint8_t a = 0;
    if(ISONE(a) == true)
    {
        printf("true");
    }
    else
    {
        printf("false");
    }
}

30. 宏定义初始化结构体

参考地址

31. 带参数宏防止重复定义的方法

  • 带参数在使用ifndef的时候是不需要参数的
#include "stdio.h"
#include "stdint.h"

//#define MAX(a,b) a>b ? a:b

#ifndef MAX // 这个不需要MAX带参数的, 至需要这个名字就可以了
#define MY_PRINT printf("no matter\r\n")
#endif

int main(void)
{
    MY_PRINT;
}

32. 优秀的代码

  • 代码高内聚, 低耦合, 尽量不要做通用的代码, 一个函数做好一件事, 一个c和h文件做好自己的事, 不要总想着通用配置,
  • 驱动尽量不要使用通用的配置, 驱动要分开来, 就算是usart1和usart2的区别, 那么还是要区分开来, 这样就不用传入那么多的参数了, 看似增加了很多的工作量, 但是能更好的理解代码, 和调用代码, 这就是区分的好处
  • 底层细分的更多, 那么上层就越容易实现, 比如分成多个usart驱动, 上层的有对应多个的串口应用层
  • 函数之间间隔2行, 分隔符上面间隔3行, 函数内部不同代码间隔1行, 不同功能的宏定义或者变量之间间隔1行

33. 返回函数指针的API

  • 以函数返回数组的首地址的指针为例子
#include "stdio.h"
#include "stdint.h"

uint8_t array[]={1,2,3,4,5,6,7,8};
uint8_t getData = 0;
uint8_t *ptr = NULL;

static uint8_t * get_RxDataBuffer_ptr(void)
{
    return array;
}

typedef struct
{
    uint8_t * (*get_RxDataBuffer_ptr)(void);
}api;

api my_api = 
{
    .get_RxDataBuffer_ptr = get_RxDataBuffer_ptr
};

int main(void)
{
    ptr = my_api.get_RxDataBuffer_ptr();

    for(int i =0; i<sizeof(array);i++)
    {
        getData = *ptr++;           // 获取指针指向的值
        printf("%d\r\n",getData);   // 打印值
    }
    return 0;
}

34. 宏定义显示数组

#include<stdio.h>
#include<stdlib.h>

#define SHOW_ARRAY(array){\
            int lengthOfArray=sizeof(array)/sizeof(array[0]);\
            for(int i=0;i<lengthOfArray;i++){\
                printf("%d\t",array[i]);\
                if((i+1)%5==0){\
                    printf("\n");\
                }\
            }\
            printf("\n");\
}

#define DISPLAY_ARRAY(str,array){\
            int lengthOfArray=sizeof(array)/sizeof(array[0]);\
            for(int i=0;i<lengthOfArray;i++){\
                printf("%s[%2d]=%d\t",str,i,array[i]);\
                if(i!=0&&(i+1)%4==0){\
                    printf("\n");\
                }\
            }\
            printf("\n");\
}

int main() {
    int vectory[] = { 53,78,35,89,23,62,22,12,62,95,27,28,8,86,32 };
    DISPLAY_ARRAY("vectory",vectory);
    printf("\n");
    SHOW_ARRAY(vectory);

    return 0;
}

1542258768896

35. 结构体数组的初始化

#include "stdio.h"
#include "stdint.h"

typedef struct
{
    uint8_t a;
    uint8_t b;
}my_struct;

my_struct test_str[2]={{1,2},{3,4}};

void main(void)
{
    for(int i = 0; i<2;i++)
    {
        printf("%d ",test_str[i].a);
        printf("%d ",test_str[i].b);
    }
}

36. 二维数组的初始化

int a[2][3]={{1,2,3},{4,5,6}};// 分别初始化

37. 一种api的方式

参考地址

38. 怎样能让串口只用一套的程序, 就能实现初始化和收发

初始化只是用结构体定参数

那么怎么调用不通的收发程序呢

39. 快速切换驱动的方式, 类似于多态, 但是跟多态有很大的不同

#include "config_save_table.h"
#include "stmflash.h"

CONFIG_SAVE_TABLE_t Config_Save_Table;

STMFLASH_API *p = &stmflash; // 这里就是把p指向了stmflash的结构体api,如果要切换到其它的api, 那么就可以改为eeprom_API *p = &eeprom;这样下面部分的内容就可以不修改了

// reload when the board power up
static void Reload(void)
{
    // read from stmflash
    p->Read(&Config_Save_Table,sizeof(Config_Save_Table));
}

static void Update(void)
{
    if(false == Config_Save_Table.update_flag) return;

    // write to the stmflash
    p->Write(&Config_Save_Table,sizeof(Config_Save_Table));
}

CONFIG_SAVE_TABLE_API_t config_save_table =
{
    .Reload  = Reload,
    .Update  = Update,
};

40. 占位符的使用方式

#include "stdint.h"
#include "stdio.h"

struct abd
{
    uint8_t a;
    uint16_t b;
    uint32_t c;
}abc;

void main(void)
{
    abc.a = 1;
    abc.b = 2;
    abc.c = 3;

    // 左对齐占位符,占5个位
    printf("|%-5d|",abc.a);
    printf("%-5d|",abc.b);
    printf("%-5d|",abc.c);
    printf("\n");

    // 右对齐占位符,占5个位
    printf("|%5d|",abc.a);
    printf("%5d|",abc.b);
    printf("%5d|",abc.c);
    printf("\n");
}

41. 关于结构体成员的初始化

#include <stdio.h>
#include <stdint.h>

typedef struct 
{
    int a;
    int b;
}str;

str str1 = 
{
    .a = 0, // 这个地方的写法, 就是先写str1.a = 0;然后将str1删除留下后面的, 好处是能自动补全
    .b = 2,
};

int main (void)
{
    printf("%d\n",str1.a);
    printf("%d\n",str1.b);
    return 0;
}

42. 表驱动的代码

#include <istream>
#include <stdio.h>

enum {
    _LOGIN_ = 1,
    _REGISTER_,
    _EXIT_
};

void DoLogin()
{
    printf("login\n");
}

void DoRegister()
{
    printf("register\n");
}

void Exit()
{
    printf("exit\n");
}

typedef void(*PFUNC)();

typedef struct funcTable {
    int choice;
    PFUNC pfunc;
}funcTable_t;

funcTable gTable[] = {
    { _LOGIN_, &DoLogin },
    { _REGISTER_, &DoRegister },
    { _EXIT_, &Exit }
};

void run(int choice)
{
    for (int i = 0; i < 3; i++) {
        if (choice == gTable[i].choice) {
            gTable[i].pfunc();
        }
    }
}

int main(void)
{
    int choice = 0;
    choice++;
    run(choice);

    choice++;
    run(choice);

    choice++;
    run(choice);
    return 0;
}

43. 表驱动结构体(是通过void函数转的)

typedef char (*PTRFUN)(int);   
PTRFUN pFun;   
char glFun(int a){ return;}   
void main()   
{   
    pFun = glFun;   
    (*pFun)(2);   
}   

44. 嵌入式自定义的printf

  • 头文件
#ifndef __DEV_PRINTF_H
#define __DEV_PRINTF_H

#include <stm32f4xx.h>

uint16_t dev_printf(const char* format, ...);

#endif /* __DEV_PRINTF_H */
  • 源文件
#include "dev_printf.h"
#include <stdarg.h>
#include <stdio.h>

#define UARTx USART2

static char stdbuff[ 512 ];

uint16_t dev_printf(const char* format, ...)
{
    uint16_t n = 0;
    va_list  ap;

    va_start(ap, format);
    n = (uint16_t)vsnprintf(stdbuff, sizeof(stdbuff), format, ap);
    va_end(ap);

    for (int i = 0; i < n; i++) {
        USART_SendData(UARTx, stdbuff[ i ]);
        while (USART_GetFlagStatus(UARTx, USART_FLAG_TC) == RESET)
            ;
    }

    return n;
}
  • 使用方法
    与printf同样的用法, 不需要改变

  • 前提条件
    编译的pc系统中有stdarg.h和stdio.h,否则va_start无法使用以及vsnprintf都无法使用

45. 16进制的字符串形式转为16进制

  • 法一: 使用sscanf
#include <stdio.h>

int main()
{
    char szValue[] = "0039";
    int nValue = 0;
    sscanf(szvalue, "%x", &nValue); // %x是指16进制
    printf("%d\n", nValue);
    return 0;
}
  • 法二: 使用strtol(字符串转长整型)
#include <stdio.h>
#inlcude <stdlib.h> // strtol头文件

int main()
{
    char *p = "0039"
    char *str;
    int i = (int)strtol(p,&str,16); // 16进制, &str可用NULL代替
    printf("%d\n", i);
    return 0;
}

46. hex转为字符串

#include <ctype.h>

int hex2Ascii(char *hex, char* ascii, uint32_t hex_len)
{
    int len = hex_len;
    int tlen;
    int i;
    for(i=0, cnt=0, tlen=0; i<len;i++)
    {
        char c = toupper(hex[i]);
        if((c>='0' && c<='9') || (c >= 'A' && c <= 'F'))
        {
            ascii[tlen++] += c;
        }
    }
    return tlen;
}

47. 分层打印的方法

#ifndef __DEBUG_H
#define __DEBUG_H

#include <stdio.h>
//#include <cstdio>

/**********************************************
 *                LOG DEFINE                   *
 **********************************************/
// OFF FATAL ERROR WARN INFO DEBUG TRACE
//  0    1     2     3    4    5     6
#define OFF   (0)
#define FATAL (1)
#define ERROR (2)
#define WARN  (3)
#define INFO  (4)
#define DEBUG (5)
#define TRACE (6)

#define LOG_LEVEL DEBUG

#define LOG(level, format, ...)      \
    if (LOG_LEVEL >= level) {        \
        printf(format, __VA_ARGS__); \
    }

#endif /* DEBUG_H */
  • 使用示例
#include "debug.h"

int main(int argc, char* argv[])
{
    char a[] = "abc";
    LOG(INFO, "%s\n", a);

    int b = 1552;
    LOG(DEBUG, "%d\n", b);

    return 0;
}

48. c和c++混合编程(__cplusplus与externnal "c"的使用)

#ifdef __cplusplus
extern "C"{
#endif
/*正常c语言的一些使用方法*/
#ifdef __cplusplus
}
#endif

吴爸爸的技术小木屋 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:c语言优秀的代码形式
喜欢 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址