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. 使用方法
- 正常的写程序
- 写一个名为
文件名_fun
的结构体, 把对外函数都放到这个结构体中来,转化为指针
- 实体化这个
文件名_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. 宏定义放置位置的原则
- 如果外部不需要使用, 那么就定义在
c文件中
- 如果外部需要使用, 那么就定义在
h文件中
26. shell的方法
- 每一个shell独占一个c文件和一个h文件, 将h文件添加到总的shell中, 总的shell文件会使用api的方式进行调用
- 添加一个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;
}
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