can总线程序讲解_详解stm32的CAN控制器(程序分享)
首先简单介绍一下CAN总线,关于CAN总线是谁发明的,CAN总线的历史,CAN总线的发展,CAN总线的应用场合,这些,通通不说。这里只是以我个人理解,简单说说CAN通信。CAN总线的端点没有地址(除非自己在帧里定义地址),CAN总线通信不用地址,用标识符,不是单独的谁发给谁,而是,你总是发送给整个网络。然后每个节点都有过滤器,对网络上的传播的帧的标识符进行过滤,自己想要什么样的帧,可以设置自己的过滤器,接收相关的帧信息。如果两个节点同时发送怎么办?这个不用我们担心,CAN控制器会自己仲裁,让高优先级的帧先发。
然后我们可以了解一下stm32的CAN控制器。
如上图所示,stm32有两个can控制器,can1(主),和can2(从),其中过滤器的设置是通过can1来设置,其他工作模式,波特率等,可以各自设置。每个控制器有三个发送邮箱,两个fifo,每个fifo有三个接收邮箱。
发送:选择一个空的发送邮箱,把帧信息写到该发送邮箱的寄存器里,请求发送,控制器就会根据标识符的优先级把帧先后发送出去。
接收:如果接收到的帧的标识符能过过滤表的一系列过滤,该帧信息就会保存在fifo接收邮箱的寄存器里。
过滤器:stm32f407共有28组过滤器,每组过滤器可以设置关联到fifo0或者fifo1,每组都包括两个32位存储器,可以配置成一个32位有位屏蔽功能的标识符过滤器,或者两个32位完全匹配的标识符过滤器,或者两个16位有位屏蔽功能的标识符过滤器,或者四个16位完全匹配的标识符过滤器。如下图所示:
我所说的完全匹配的意思是,接收到的帧的标识符每一位都要跟过滤器对应的位一样,才能过得了这个过滤器。有位屏蔽功能的意思是一个寄存器放标识符,一个放屏蔽掩码,屏蔽掩码为1的位对应的接收到的帧的标识符的位与对应的放标识符的寄存器的位一致,就能通过。
传输一位的时间和波特率的计算:
CAN控制器的波特率是由APB时钟线和CAN位时序寄存器CAN_BTR的TS2[3:0]、TS1[2:0]和BRP[9:0]确定的,其中,TS1[2:0]定义了时间段1占用多少个时间单元,TS2[3:0]定义了时间段2占用多少个时间单元,BRP[9:0]定义对APB1时钟的分频。
PS:设置波特率为1M
其中Tpclk为APB1的时钟周期,假设为
Tpclk = 1/42M
0≦TS1≦7
0≦TS2≦15
0≦BRP≦1021
根据以上数据,有
(TS2+TS1+3)(BRP+1)=42
令BRP=2,有
TS2+TS1=11
令TS1=8,TS2=3
设置步骤:
1. 设置中断优先级分组(如果之前没有设置),这个最好一个程序里只在开头设置一次。
2. 使能相关GPIO时钟。
3. 选择相关GPIO引脚的复用功能。
4. 设置相关GPIO引脚为复用模式。
5. 设置相关GPIO引脚的速度,方式。
6. 设置主控制寄存器MCR,进入初始化模式
7. 等待进入初始化模式
8. 设置波特率。
9. 其他设置。
10. 如果要用到中断,在中断使能寄存器IER中使能相关中断响应。
11. 如果要用到中断,设置相关中断优先级(NVIC_IP)。
12. 如果要用到中断,使能相关中断(NVIC_ISER)。
13. 设置主控制寄存器MCR,进入正常工作模式。
14. 设置FMR,使过滤器组工作在初始化模式。
15. 设置FMR的CAN2SB,确定CAN2的过滤器组从哪一组开始。
16. 设置用到的过滤器组的工作方式。
17. 设置用到的过滤器组的位宽。
18. 给fifo0和fifo2划分(关联)过滤组。
19. 禁用用到的过滤器组。
20. 设置过滤器组的标识符,帧类型等。
21. 使能相关过滤器组。
22. 设置FMR,使过滤器组工作在正常模式。
23. 如果要用中断,编写中断服务函数(函数名是固定的)。
24. 中断服务函数里检查是哪个中断。
25. 编写相应服务程序。
电路请参见本博客:小工具之——CAN收发器
程序:
[plain] view plaincopy/************************************
标题:操作CAN的练习
软件平台:IAR for ARM6.21
硬件平台:stm32f4-discovery
主频:168M
描述:通过硬件收发器连接CAN1,CAN2
组成一个两个端点的网络
CAN1循环发出数据帧
CAN2接收过滤数据帧
用uart把CAN2接收到
的数据帧发到超级终端
author:小船
data:2012-08-14
*************************************/
#include 《stm32f4xx.h》
#include “MyDebugger.h”
#define RECEIVE_BUFFER_SIZE 20
u32 CAN2_receive_buffer[RECEIVE_BUFFER_SIZE][4];
u8 UART_send_buffer[1800];
u8 Consumer = 0;
u8 Producer = 0;
u32 Gb_TimingDelay;
void Delay(uint32_t nTime);
void TIM7_init();//定时1s
u32 get_rece_data();
void CAN_GPIO_config();
void main ()
{
u32 empty_box;
SysTick_Config(SystemCoreClock / 1000); //设置systemtick一毫秒中断
SCB-》AIRCR = 0x05FA0000 | 0x400; //中断优先级分组 抢占:响应=3:1
MyDebugger_Init();
TIM7_init();
MyDebugger_Message( “\n\rtesting.。。.。。\n\r” ,
sizeof(“\n\rtesting.。。.。。\n\r”)/sizeof(char) );
CAN_GPIO_config();
RCC-》APB1ENR |= ((1《《25)|(1《《26));//使能CAN1、CAN2时钟
CAN1-》MCR = 0x00000000;
/*
请求进入初始化模式
禁止报文自动重传
自动唤醒模式
*/
CAN1-》MCR |= ((1《《0)|(1《《4)|(1《《5));
CAN1-》MCR &= ~(1《《16);//在调试时,CAN照常工作
while(!(CAN1-》MSR & 0xfffffffe)) //等待进入初始化模式
{
MyDebugger_LEDs(orange, on);
}
MyDebugger_LEDs(orange, off);
/*
正常模式
重新同步跳跃宽度(1+1)tq
TS2[2:0]=3
TS1[3:0]=8
BRP[9:0]=2
ps:
tq = (BRP[9:0] + 1) x tPCLK,
tBS2 = tq x (TS2[2:0] + 1),
tBS1 = tq x (TS1[3:0] + 1),
NominalBitTime = 1 × tq+tBS1+tBS2,
BaudRate = 1 / NominalBitTime
波特率设为1M
*/
CAN1-》BTR = ((0《《30)|(0x01《《24)|(3《《20)|(8《《16)|(2《《0));
CAN1-》MCR &= ~(0x00000001);//正常工作模式
CAN2-》MCR = 0x00000000;
/*
请求进入初始化模式
禁止报文自动重传
自动唤醒模式
*/
CAN2-》MCR |= ((1《《0)|(1《《4)|(1《《5));
CAN2-》MCR &= ~(1《《16);//在调试时,CAN照常工作
while(!(CAN2-》MSR & 0xfffffffe)) //等待进入初始化模式
{
MyDebugger_LEDs(orange, on);
}
MyDebugger_LEDs(orange, off);
/*
正常模式
重新同步跳跃宽度(1+1)tq
TS2[2:0]=3
TS1[3:0]=8
BRP[9:0]=2
ps:
tq = (BRP[9:0] + 1) x tPCLK,
tBS2 = tq x (TS2[2:0] + 1),
tBS1 = tq x (TS1[3:0] + 1),
NominalBitTime = 1 × tq+tBS1+tBS2,
BaudRate = 1 / NominalBitTime
波特率设为1M
*/
CAN2-》BTR = ((0《《30)|(0x01《《24)|(3《《20)|(8《《16)|(2《《0));
CAN2-》IER &= 0x00000000;
/*
FIFO1消息挂号中断使能
FIFO1满中断使能
FIFO1溢出中断使能
*/
CAN2-》IER |= ((1《《4)|(1《《5)|(1《《6));
NVIC-》IP[65] = 0xa0; //抢占优先级101,响应优先级0
NVIC-》ISER[2] |= (1《《1); //使能中断线65,也就是can2_rx1中断
CAN2-》MCR &= ~(0x00000001);//正常工作模式
//总共有28组过滤器
CAN1-》FMR |= 1; //过滤器组工作在初始化模式
CAN1-》FMR &= 0xffffc0ff;//CAN2的过滤器组从14开始
CAN1-》FMR |= (14《《8);
CAN1-》FM1R |= (1《《14);//过滤器组14的寄存器工作在标识符列表模式
//位宽为16位,2个32位分为四个16位寄存器,过滤四个标识符
//CAN1-》FS1R |= (1《《15);//过滤器组15为单个32位寄存器,用于扩展标识符
CAN1-》FFA1R = 0x0fffc000;//0~13号过滤器组关联到fifo0,14~27号过滤器组关联到fifo1
CAN1-》FA1R &= ~(1《《14);//禁用过滤器组14
/*
过滤器组0寄存器分为4个十六位过滤器:
标识符列表:
过滤器编号 匹配标准标识符 RTR IDE EXID[17:15]
0 0x7cb(111 1100 1011b) 数据帧 标准标识符 000b
1 0x4ab(100 1010 1011b) 数据帧 标准标识符 000b
2 0x7ab(111 1010 1011b) 数据帧 标准标识符 000b
3 0x40b(100 0000 1011b) 数据帧 标准标识符 000b
*/
CAN1-》sFilterRegister[14].FR1 &= 0x00000000;
CAN1-》sFilterRegister[14].FR2 &= 0x00000000;
CAN1-》sFilterRegister[14].FR1 |= ((0x7cb《《5)|(0《《4)|(0《《3));
CAN1-》sFilterRegister[14].FR1 |= ((0x4ab《《21)|(0《《20)|(0《《19));
CAN1-》sFilterRegister[14].FR2 |= ((0x7ab《《5)|(0《《4)|(0《《3));
CAN1-》sFilterRegister[14].FR2 |= ((0x40b《《21)|(0《《20)|(0《《19));
CAN1-》FA1R |= (1《《14);//使能过滤器组14
CAN1-》FMR &= ~1; //过滤器组正常工作
while(1)
{
/*
选择空的发送邮箱:
标准标识符0x7ab(111 1010 1011b)
数据帧
不使用扩展标识符
*/
if( CAN1-》TSR & ((1《《26)|(1《《27)|(1《《28)) )
{
empty_box = ((CAN1-》TSR》》24) & 0x00000003);
CAN1-》sTxMailBox[empty_box].TIR = (0x7ab《《21);
CAN1-》sTxMailBox[empty_box].TDTR &= 0xfffffff0;
CAN1-》sTxMailBox[empty_box].TDTR |= 0x00000008;//发送数据长度为8
CAN1-》sTxMailBox[empty_box].TDLR = 0x12345678;
CAN1-》sTxMailBox[empty_box].TDHR = 0x9abcdef0;
CAN1-》sTxMailBox[empty_box].TIR |= (1《《0);//请求发送
}
else
{
MyDebugger_LEDs(orange, on);
}
Delay(100);
/*
选择空的发送邮箱:
标准标识符0x4ab(100 1010 1011b)
数据帧
不使用扩展标识符
*/
if( CAN1-》TSR & ((1《《26)|(1《《27)|(1《《28)) )
{
empty_box = ((CAN1-》TSR》》24) & 0x00000003);
CAN1-》sTxMailBox[empty_box].TIR = (0x4ab《《21);
CAN1-》sTxMailBox[empty_box].TDTR &= 0xfffffff0;
CAN1-》sTxMailBox[empty_box].TDTR |= 0x00000008;//发送数据长度为8
CAN1-》sTxMailBox[empty_box].TDLR = 0x56781234;
CAN1-》sTxMailBox[empty_box].TDHR = 0x9abcdef0;
CAN1-》sTxMailBox[empty_box].TIR |= (1《《0);//请求发送
}
else
{
MyDebugger_LEDs(orange, on);
}
Delay(100);
/*
选择空的发送邮箱:
标准标识符0x7cb(100 1010 1011b)
数据帧
不使用扩展标识符
*/
if( CAN1-》TSR & ((1《《26)|(1《《27)|(1《《28)) )
{
empty_box = ((CAN1-》TSR》》24) & 0x00000003);
CAN1-》sTxMailBox[empty_box].TIR = (0x7cb《《21);
CAN1-》sTxMailBox[empty_box].TDTR &= 0xfffffff0;
CAN1-》sTxMailBox[empty_box].TDTR |= 0x00000006;//发送数据长度为6
CAN1-》sTxMailBox[empty_box].TDLR = 0x56781234;
CAN1-》sTxMailBox[empty_box].TDHR = 0x00009abc;
CAN1-》sTxMailBox[empty_box].TIR |= (1《《0);//请求发送
}
else
{
MyDebugger_LEDs(orange, on);
}
Delay(100);
/*
选择空的发送邮箱:
标准标识符0x40b(100 0000 1011b)
数据帧
不使用扩展标识符
*/
if( CAN1-》TSR & ((1《《26)|(1《《27)|(1《《28)) )
{
empty_box = ((CAN1-》TSR》》24) & 0x00000003);
CAN1-》sTxMailBox[empty_box].TIR = (0x40b《《21);
CAN1-》sTxMailBox[empty_box].TDTR &= 0xfffffff0;
CAN1-》sTxMailBox[empty_box].TDTR |= 0x00000004;//发送数据长度为4
CAN1-》sTxMailBox[empty_box].TDLR = 0x56781234;
CAN1-》sTxMailBox[empty_box].TDHR = 0x00000000;
CAN1-》sTxMailBox[empty_box].TIR |= (1《《0);//请求发送
}
else
{
MyDebugger_LEDs(orange, on);
}
Delay(100);
}
}
/****************************************
函数名:CAN_GPIO_config
参数:无
返回值:无
功能:设置CAN1,2控制器用到IO口
CAN1_TX---------PD1
CAN1_RX---------PB8
CAN2_TX---------PB13
CAN2_RX---------PB5
****************************************/
void CAN_GPIO_config()
{
RCC-》AHB1ENR |= ((1《《1) | (1《《3));//使能GPIOB、D时钟
GPIOB-》AFR[0] |= 0x00900000; //AF9
GPIOB-》AFR[1] |= 0x00900009;
GPIOD-》AFR[0] |= 0x00000090;
GPIOB-》MODER &= 0xF3FCF3FF; //第二功能
GPIOB-》MODER |= 0x08020800;
GPIOD-》MODER &= 0xFFFFFFF3;
GPIOD-》MODER |= 0x00000008;
GPIOB-》OSPEEDR &= 0xF3FCF3FF; //50M
GPIOB-》OSPEEDR |= 0x08020800;
GPIOD-》OSPEEDR &= 0xFFFFFFF3;
GPIOD-》OSPEEDR |= 0x00000008;
GPIOB-》PUPDR &= 0xF3FCF3FF; //上拉
GPIOB-》PUPDR |= 0x04010400;
GPIOD-》PUPDR &= 0xFFFFFFF3;
GPIOD-》PUPDR |= 0x00000004;
}
/****************************************
函数名:CAN2_RX1_IRQHandler
参数:无
返回值:无
功能:CAN2fifo1接收中断处理
把信息存进循环队列
****************************************/
void CAN2_RX1_IRQHandler()
{
if(CAN2-》RF1R & (0x00000003))//接收到新的消息,fifo1非空
{
Producer++;
if(Producer == RECEIVE_BUFFER_SIZE)Producer = 0;
if(Producer != Consumer)
{
CAN2_receive_buffer[Producer][0] = CAN2-》sFIFOMailBox[1].RIR;
CAN2_receive_buffer[Producer][1] = CAN2-》sFIFOMailBox[1].RDTR;
CAN2_receive_buffer[Producer][2] = CAN2-》sFIFOMailBox[1].RDLR;
CAN2_receive_buffer[Producer][3] = CAN2-》sFIFOMailBox[1].RDHR;
}
else
{
if(Producer == 0)Producer = RECEIVE_BUFFER_SIZE;
Producer--;
MyDebugger_LEDs(blue, on);
}
CAN2-》RF1R |= (1《《5);//释放邮箱
}
if(CAN2-》RF1R & (1《《3))//fifo0满
{
MyDebugger_LEDs(red, on);
CAN2-》RF1R &= ~(1《《3);
}
if(CAN2-》RF1R & (1《《4))//fifo0溢出
{
MyDebugger_LEDs(red, on);
CAN2-》RF1R &= ~(1《《4);
}
}
/****************************************
函数名:TIM7_init
参数:无
返回值:无
功能:初始化定时器7
作1s定时用
****************************************/
void TIM7_init()
{
RCC-》APB1ENR |= (1《《5); //打开TIM7时钟
TIM7-》PSC = 8399; //对时钟84M进行8400分频,使得计数频率为10k
TIM7-》ARR = 10000; //定时一秒
TIM7-》CNT = 0; //清空计数器
TIM7-》CR1 |= (1《《7); //自动重装载预装载使能
TIM7-》DIER |= 1; //使能中断
NVIC-》IP[55] = 0xe0;
NVIC-》ISER[1] |= (1《《(55-32));
TIM7-》CR1 |= 1; //开始计时
}
/****************************************
函数名:TIM7_IRQHandler
参数:无
返回值:无
功能:定时器7中断处理
1s定时到
把can2收到的信息转换格式
用usrt发送到超级终端显示
****************************************/
void TIM7_IRQHandler(void)
{
u32 length;
if(TIM7-》SR)
{
length = get_rece_data();
MyDebugger_Message( UART_send_buffer, length );
TIM7-》SR &= ~(0x0001);
}
}
/****************************************
函数名:get_rece_data
参数:无
返回值:length 整理后要发送数据的长度
功能:把循环队列的信息取出
进行格式转换
把信息存到uart发送缓冲区
****************************************/
u32 get_rece_data()
{
u8 filter_No;
u8 Data_length;
char i;
u32 length = 0;
const char ascii[16] = {‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’,
‘8’, ‘9’, ‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’};
while(1)
{
if(Producer != Consumer)
{
Consumer++;
if(Consumer == RECEIVE_BUFFER_SIZE)Consumer=0;
UART_send_buffer[length++] = ‘\n’;
UART_send_buffer[length++] = ‘\r’;
//Filter No.xx
UART_send_buffer[length++] = ‘F’;
UART_send_buffer[length++] = ‘i’;
UART_send_buffer[length++] = ‘l’;
UART_send_buffer[length++] = ‘t’;
UART_send_buffer[length++] = ‘e’;
UART_send_buffer[length++] = ‘r’;
UART_send_buffer[length++] = ‘ ’;
UART_send_buffer[length++] = ‘N’;
UART_send_buffer[length++] = ‘o’;
UART_send_buffer[length++] = ‘。’;
filter_No = (CAN2_receive_buffer[Consumer][1]》》8) & 0x000000ff;
UART_send_buffer[length++] = filter_No%100/10 + ‘0’;
UART_send_buffer[length++] = filter_No%10 + ‘0’;
UART_send_buffer[length++] = ‘\n’;
UART_send_buffer[length++] = ‘\r’;
//DataLength:x
UART_send_buffer[length++] = ‘D’;
UART_send_buffer[length++] = ‘a’;
UART_send_buffer[length++] = ‘t’;
UART_send_buffer[length++] = ‘a’;
UART_send_buffer[length++] = ‘L’;
UART_send_buffer[length++] = ‘e’;
UART_send_buffer[length++] = ‘n’;
UART_send_buffer[length++] = ‘g’;
UART_send_buffer[length++] = ‘t’;
UART_send_buffer[length++] = ‘h’;
UART_send_buffer[length++] = ‘:’;
Data_length = CAN2_receive_buffer[Consumer][1] & 0x0000000f;
UART_send_buffer[length++] = Data_length % 10 + ‘0’;
UART_send_buffer[length++] = ‘\n’;
UART_send_buffer[length++] = ‘\r’;
if(CAN2_receive_buffer[Consumer][0] & (1《《1))
{
UART_send_buffer[length++] = ‘R’;
UART_send_buffer[length++] = ‘e’;
UART_send_buffer[length++] = ‘m’;
UART_send_buffer[length++] = ‘o’;
UART_send_buffer[length++] = ‘t’;
UART_send_buffer[length++] = ‘e’;
UART_send_buffer[length++] = ‘F’;
UART_send_buffer[length++] = ‘r’;
UART_send_buffer[length++] = ‘a’;
UART_send_buffer[length++] = ‘m’;
UART_send_buffer[length++] = ‘e’;
}
else
{
UART_send_buffer[length++] = ‘D’;
UART_send_buffer[length++] = ‘a’;
UART_send_buffer[length++] = ‘t’;
UART_send_buffer[length++] = ‘a’;
UART_send_buffer[length++] = ‘F’;
UART_send_buffer[length++] = ‘r’;
UART_send_buffer[length++] = ‘a’;
UART_send_buffer[length++] = ‘m’;
UART_send_buffer[length++] = ‘e’;
}
UART_send_buffer[length++] = ‘\n’;
UART_send_buffer[length++] = ‘\r’;
if(CAN2_receive_buffer[Consumer][0] & (1《《2))
{
UART_send_buffer[length++] = ‘e’;
UART_send_buffer[length++] = ‘x’;
UART_send_buffer[length++] = ‘t’;
UART_send_buffer[length++] = ‘ ’;
UART_send_buffer[length++] = ‘I’;
UART_send_buffer[length++] = ‘D’;
UART_send_buffer[length++] = ‘:’;
UART_send_buffer[length++] =
ascii[CAN2_receive_buffer[Consumer][0] 》》 31];
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][0] 》》 27)& 0x0000000f];
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][0] 》》 23)& 0x0000000f];
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][0] 》》 19)& 0x0000000f];
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][0] 》》 15)& 0x0000000f];
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][0] 》》 11)& 0x0000000f];
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][0] 》》 7)& 0x0000000f];
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][0] 》》 3)& 0x0000000f];
}
else
{
UART_send_buffer[length++] = ‘s’;
UART_send_buffer[length++] = ‘t’;
UART_send_buffer[length++] = ‘d’;
UART_send_buffer[length++] = ‘ ’;
UART_send_buffer[length++] = ‘I’;
UART_send_buffer[length++] = ‘D’;
UART_send_buffer[length++] = ‘:’;
UART_send_buffer[length++] =
ascii[CAN2_receive_buffer[Consumer][0] 》》 29];
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][0] 》》 25)& 0x0000000f];
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][0] 》》 21)& 0x0000000f];
}
UART_send_buffer[length++] = ‘\n’;
UART_send_buffer[length++] = ‘\r’;
UART_send_buffer[length++] = ‘D’;
UART_send_buffer[length++] = ‘a’;
UART_send_buffer[length++] = ‘t’;
UART_send_buffer[length++] = ‘a’;
UART_send_buffer[length++] = ‘:’;
if(Data_length 》 4)
{
for(i = 2*Data_length - 8; i 》 0; i--)
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][3] 》》 ((i-1)*4))& 0x0000000f];
for(i = 8; i 》 0; i--)
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][2] 》》 ((i-1)*4))& 0x0000000f];
}
else
{
for(i = 2*Data_length; i 》 0; i--)
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][2] 》》 ((i-1)*4))& 0x0000000f];
}
UART_send_buffer[length++] = ‘\n’;
UART_send_buffer[length++] = ‘\r’;
}
else
break;
}
return length;
}
void Delay(uint32_t nTime)
{
Gb_TimingDelay = nTime;
while(Gb_TimingDelay != 0);
}
void SysTick_Handler(void)
{
if (Gb_TimingDelay != 0x00)
{
Gb_TimingDelay--;
}
}
运行结果:
打开APP精彩内容
点击阅读全文
can总线程序讲解_详解stm32的CAN控制器(程序分享)相关推荐
- java web底层原理_详解Java开发Web应用程序的底层原理
前言 前面一篇文章,我从整个应用程序的整体以及跟运行环境的关系简单聊了一下我们现在常用的Spring框架的设计基础和准则,其中主要是控制反转和依赖注入,以及容器化编程等概念. 这里我不想去复述这些概念 ...
- pwm一个时间单位_详解STM32的PWM输出及频率和脉宽(占空比)的计算——寄存器配置六步曲!...
一.stm32的pwm输出引脚是使用的IO口的复用功能. 二.T2~T5这4个通用定时器均可输出4路PWM--CH1~CH4. 三.我们以tim3的CH1路pwm输出为例来进行图文讲解(其它类似),并 ...
- esp32 怎么分配freertos 堆栈大小_详解STM32单片机的堆栈
学习STM32单片机的时候,总是能遇到"堆栈"这个概念.分享本文,希望对你理解堆栈有帮助. 对于了解一点汇编编程的人,就可以知道,堆栈是内存中一段连续的存储区域,用来保存一些临时数 ...
- python网络编程讲解_详解Python Socket网络编程
Socket 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的,例如我们每天浏览网页.QQ ...
- spwm调制c语言程序,SPWM基本原理详解(图文并茂+公式推导+C程序实现)
基本原理 SPWM的全称是(Sinusoidal PWM),正弦脉冲宽度调制是一种非常成熟,使用非常广泛的技术: 之前在PWM的文章中介绍过,基本原理就是面积等效原理,即冲量相等而形状不同的窄脉冲加在 ...
- 24c02读程序C语言详解,24C02读写C语言程序
24C02读写操作程序.c >下面是本人编写的源程序,已经调试成功,下载就可以使用,程序编写的不是很规范 ,希望各位读者批评指正!!! >/************************ ...
- 对hash签名失败_详解Vue开发微信H5微信分享签名失败问题解决方案
关于Vue中路由使用history模式,开发微信H5页面分享时在安卓上签名有效成功,但是在IOS设备上一直报错签名失效问题 问题描述:在Vue开发过程中,路由使用History模式下,在使用微信分享时 ...
- 本地缓存需要高时效性怎么办_详解微信小程序缓存--缓存时效性
关于本地缓存 1.wx.setStorage(wx.setStorageSync).wx.getStorage(wx.getStorageSync).wx.clearStorage(wx.clearS ...
- 3000门徒内部训练绝密视频(泄密版)第8课:彻底实战详解使用IDE开发Spark程序
彻底实战详解使用IDE开发Spark程序 使用IDE开发Spark分析 使用IDE开发Spark实战 使用IDE开发Spark的Local和Cluster 开发两种选择:IDEA.Eclipse 下载 ...
最新文章
- python 调用函数 开销_参数中带有函数的Python日志记录开销
- 分苹果问题的C++和Python实现
- Android开发之使用Preferences设计软件设置界面(源代码分享)
- gops - Go语言程序查看和诊断工具
- STL自定义排序函数 需要注意的问题
- 16s及宏基因组测序公司资源--20161104
- A different twist on pre-compiling JSPs--reference
- Crazepony的理念
- 32f4 usb 升级程序_不断中招的你还放心升级win10吗?wi10近期更新问题及解决办法...
- spring 随笔(一) bean Dependency Injection
- Scipy教程 - 线性代数库scipy.linalg
- 【c语言】打印出100以内奇数
- 利用kali Linux破解WiFi密码
- Linux系统屏幕出现错位重影,win10屏幕出现错位重影怎么办
- Golang环境配置及第三方库安装使用(至关重要的防踩坑篇)
- 【自学】张量、维度、多维矩阵、dim、torch.argmax()
- TensorFlow中图(graphs)概念
- Java 基于WEB的农产品销售管理系统源码+数据库+论文文档+项目辅导视频
- JavaFX之Scene Builder的使用(开发一款GUI小工具原来这么简单)
- 后台系统登录一般流程
热门文章
- 手机硬件检测工具 AIDA64
- unturned服务器怎么自定义,《Unturned》机房服务器开服方法图文教程
- 基于zui的后台管理系统-麦穗博客
- NGN技术介绍及发展前景
- Unity 之 实现老虎机滚动抽奖效果
- 纠结!分布式锁到底用Redis好还是ZooKeeper好?
- DA14580笔记(2)-------睡眠状态下按键唤醒
- Ubuntu18.04安装部署GitLab-ce(HTTP/HTTPS访问,SAML配置)附:docker gitlab-ce部署
- jdbc-(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY)总结
- jenkins连接mysql_mysql – 如何将JDBC驱动程序添加到Jenkins管道?