所见即所得Web可视化编程开发板源程序分享

回复

现场总线/以太网/modbus开 发起了问题 • 1 人关注 • 0 个回复 • 165 次浏览 • 2019-10-05 11:19 • 来自相关话题

RTC时间不更新

回复

jialian4213 发起了问题 • 2 人关注 • 0 个回复 • 116 次浏览 • 2019-09-16 15:55 • 来自相关话题

STM32HAL库SPI的16位数据中断发送与接收

老是忙 发表了文章 • 0 个评论 • 2384 次浏览 • 2019-01-29 11:56 • 来自相关话题

HAL库的SPI发送接收函数的确令人迷惑,明明支持16位传输,却必须使用8位的指针。如果没能正确理解SPI发送接收函数,很容易导致程序接入HardFault_Handler中断死循环。    最近用到STM32F407的HAL编程,SPI通讯的外设要求16位通讯,对HAL库的SPI的16位通讯做了个深入研究。发现HAL库提供的函数除了入口参数含义不太明确,还是很好用的。下面以中断方式发送接收
uint16_t 数据为例,阐述函数调用的过程和要点。   1、
首先,建立发送和接收缓冲区,用16位的数组: uint16_t SPI_TxBuff[1],
SPI_RxBuff[1];   2、SPI初始化: HAL_SPI_DeInit(&hspi1);   //SPI1 复位 hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Direction =
SPI_DIRECTION_2LINES;   //双向通讯 hspi1.Init.DataSize =
SPI_DATASIZE_16BIT;       //16位传输 hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; hspi1.Init.NSS = SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler =
SPI_BAUDRATEPRESCALER_16; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi1.Init.TIMode = SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation =
SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial = 10; if (HAL_SPI_Init(&hspi1) != HAL_OK) {   Error_Handler(); } 其中,DataSize 设为16位。  3、在程序中的调用: SPI_TxBuff[0] =
data;   //data
是uint16_t 类型,复制到发送缓存。 HAL_SPI_TransmitReceive_IT(&hspi1,
(uint8_t*)SPI_TxBuff, (uint8_t*)SPI_RxBuff, 1);
//中断方式的传输 注意1:一定要用 (uint8_t*)
临时改变SPI_TxBuff和SPI_RxBuff的类型。 经对函数内部分析,实际执行时,将把这两个指针重新变换为( uint16_t *) 。 注意2:DataSize 必须是按 uint16_t
数据的数量来设置,而不是按uint8_t, 。此例中,要传输一个16位数据,所以 DataSize 设为1 。.  4、修改SPI中断程序(在stm32f4xx_it.c中)。void SPI1_IRQHandler(void){  HAL_SPI_IRQHandler(&hspi1);    //如果接收完毕,处理数据   if(HAL_SPI_GetState(&hspi1) == HAL_SPI_STATE_READY)   {      IT_ProcessingData();   //接收完成的数据已经保存在 SPI_RxBuff[0], 调用此函数进行处理   } }  注意:
HAL_SPI_TransmitReceive_IT() 函数将产生两次中断!
第一次开始发送(接收)数据;当发送(接收)完成后产生第二次中断。我们在中断程序中,用     HAL_SPI_GetState(&hspi1) == HAL_SPI_STATE_READY来判断是否是第二次,也就是传输完成后的中断,是的话,就调用数据处理函数。 查看全部

HAL库的SPI发送接收函数的确令人迷惑,明明支持16位传输,却必须使用8位的指针。如果没能正确理解SPI发送接收函数,很容易导致程序接入HardFault_Handler中断死循环。

    最近用到STM32F407的HAL编程,SPI通讯的外设要求16位通讯,对HAL库的SPI的16位通讯做了个深入研究。发现HAL库提供的函数除了入口参数含义不太明确,还是很好用的。下面以中断方式发送接收
uint16_t 数据为例,阐述函数调用的过程和要点。

   1、
首先,建立发送和接收缓冲区,用16位的数组:

 uint16_t SPI_TxBuff[1],
SPI_RxBuff[1];


   2、SPI初始化:

 HAL_SPI_DeInit(&hspi1);   //SPI1 复位

 hspi1.Instance = SPI1;
 hspi1.Init.Mode = SPI_MODE_MASTER;
 hspi1.Init.Direction =
SPI_DIRECTION_2LINES;   //双向通讯
 hspi1.Init.DataSize =
SPI_DATASIZE_16BIT;       //16位传输
 hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
 hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
 hspi1.Init.NSS = SPI_NSS_SOFT;
 hspi1.Init.BaudRatePrescaler =
SPI_BAUDRATEPRESCALER_16;
 hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
 hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
 hspi1.Init.CRCCalculation =
SPI_CRCCALCULATION_DISABLE;
 hspi1.Init.CRCPolynomial = 10;
 if (HAL_SPI_Init(&hspi1) != HAL_OK)
 {
   Error_Handler();
 }

 其中,DataSize 设为16位。


  3、在程序中的调用:

 SPI_TxBuff[0] =
data;   //data
是uint16_t 类型,复制到发送缓存。
 HAL_SPI_TransmitReceive_IT(&hspi1,
(uint8_t*)SPI_TxBuff, (uint8_t*)SPI_RxBuff, 1);
//中断方式的传输


 注意1:一定要用 (uint8_t*)
临时改变SPI_TxBuff和SPI_RxBuff的类型。 经对函数内部分析,实际执行时,将把这两个指针重新变换为( uint16_t *) 。

 注意2:DataSize 必须是按 uint16_t
数据的数量来设置,而不是按uint8_t, 。此例中,要传输一个16位数据,所以 DataSize 设为1 。.


  4、修改SPI中断程序(在stm32f4xx_it.c中)。

void SPI1_IRQHandler(void)
{
  HAL_SPI_IRQHandler(&hspi1);
 
   //如果接收完毕,处理数据
   if(HAL_SPI_GetState(&hspi1) == HAL_SPI_STATE_READY)
   {
      IT_ProcessingData();   //接收完成的数据已经保存在 SPI_RxBuff[0], 调用此函数进行处理
   }
 
}

  注意:
HAL_SPI_TransmitReceive_IT() 函数将产生两次中断!
第一次开始发送(接收)数据;当发送(接收)完成后产生第二次中断。我们在中断程序中,用

     HAL_SPI_GetState(&hspi1) == HAL_SPI_STATE_READY

来判断是否是第二次,也就是传输完成后的中断,是的话,就调用数据处理函数。



CubeMX使用高级定时的重复计数功能实现制定个数脉冲PWM

回复

乌龟也会飞 发起了问题 • 2 人关注 • 0 个回复 • 1275 次浏览 • 2019-01-28 16:56 • 来自相关话题

(转)HID+CDC复合设备在WIN10的识别问题

admin 发表了文章 • 0 个评论 • 2866 次浏览 • 2018-06-17 09:20 • 来自相关话题

1 问题现象有客户使用STM32F405并参照ST官方USB标准库下的HID+CDC的示例代码做产品,发现在WIN7上使用得好好的,可放到WIN10上,CDC第一次能够识别,再次拔插后就不能再识别,且此后无论插拔多少次都无法再识别,除非再次上电,又会重复上述现象,只有板子上电后第一次才能正确被识别,后续均不行。2 问题分析客户使用 ST官方示例代码STM32_USB-Host-Device_Lib_V2.2.0\Project\USB_Device_Examples\Composite_Examples\CDC_HID_Composite(下载页面:http://www.stmicroelectronics.com.cn/content/st_com/en/products/embedded-software/mcus-embedded-software/stm32-embedded-software/stm32-standard-peripheral-library-expansion/stsw-stm32046.html)当我尝试使用此示例代码重现客户所遇到的问题时,发现此代码在WIN7运行OK,但与客户不同的是,我测试到的情况是在WIN10下CDC一次都无法识别,HID却一直可以识别。下面来分析下问题,既然WIN7下HID和CDC都能正常识别,放在WIN10上才不正常,那么初步可以判断,此问题可能与WIN10操作系统的USB主机驱动实现有关。通过USB分析仪分析客户代码在WIN10下USB枚举异常的数据通讯:Figure 1 第一次USB枚举过程上图是客户代码第一次正常枚举的通讯数据,从图中可以看出,WIN10 USB主机在正常获取HID报告描述符后,紧接着会获取虚拟串口状态和设置波特率,这样就正常枚举结束了。我们再来看看采集到的异常USB枚举过程:Figure 2 异常枚举过程上图是WIN10下异常枚举过程。从图中可以看出,WIN10系统上USB主机在获取到设备描述符和配置描述符后直接将设备挂起了。很明显,WIN10系统的USB主机驱动实现对设备描述符或者配置描述符的内容并不认可,才会导致无法识别HID+CDC复合设备。我们不妨检查下客户代码中的设备描述符:Figure 3 获取的设备描述符复合设备的class,subclass,protocol必须为0xef,0x02,0x01,这里VID=0x0483,PID=0x3256(Cube库下为0x5740,但这个不重要),接下来看配置描述符:Figure 4 win10不能识别的配置描述符由此可见,客户的描述符是HID interface + IAD + CDC interfaces结构。对于WIN7,这种结构可以识别,但对于WIN10,这种结构WIN10未必能够兼容,我们尝试在HID interface外部加上一层IAD结构,使其成为IAD1 + HID interface + IAD2 + CDC interfaces结构,此时客户的问题得以解决,在WIN10也可以正确识别了,修改后的描述符结构如下:Figure 5 win10能够正确识别的配置描述符结束本篇实战经验之前,让我们再次回顾IAD的概念:IAD(Interface Association Descriptor),为USB设备定义了一个标准来表述捆绑在一个逻辑功能(比如这里的CDC虚拟串口)上的多个接口的聚合的方法。USB协会分配了一个设备级别的类编码(即图3中0xEF),使用IAD的设备必须使用它(如图3的设备描述符);这样可以很容易在设备枚举时就能识别出采用了IAD的设备。IAD描述符通常放在它所要捆绑的多个接口的接口描述符之前。3 结论 在WIN10系统中,建议复合设备每个逻辑功能的接口描述符前都搭载一个IAD描述符,不论这个逻辑功能是单个接口描述符完成(比如这里的HID功能)还是要由多个接口描述符完成(比如这里的CDC功能)。本文转自:https://mp.weixin.qq.com/s/9ot9AVTN4zSMAyudVDeMzA 感谢【STM32单片机】分享的经验。 查看全部

1 问题现象

有客户使用STM32F405并参照ST官方USB标准库下的HID+CDC的示例代码做产品,发现在WIN7上使用得好好的,可放到WIN10上,CDC第一次能够识别,再次拔插后就不能再识别,且此后无论插拔多少次都无法再识别,除非再次上电,又会重复上述现象,只有板子上电后第一次才能正确被识别,后续均不行。

2 问题分析

客户使用 ST官方示例代码

STM32_USB-Host-Device_Lib_V2.2.0\Project\USB_Device_Examples\Composite_Examples\CDC_HID_Composite

(下载页面:

http://www.stmicroelectronics.com.cn/content/st_com/en/products/embedded-software/mcus-embedded-software/stm32-embedded-software/stm32-standard-peripheral-library-expansion/stsw-stm32046.html)

当我尝试使用此示例代码重现客户所遇到的问题时,发现此代码在WIN7运行OK,但与客户不同的是,我测试到的情况是在WIN10下CDC一次都无法识别,HID却一直可以识别。


下面来分析下问题,既然WIN7下HID和CDC都能正常识别,放在WIN10上才不正常,那么初步可以判断,此问题可能与WIN10操作系统的USB主机驱动实现有关。

通过USB分析仪分析客户代码在WIN10下USB枚举异常的数据通讯:

捕获.PNG

Figure 1 第一次USB枚举过程


上图是客户代码第一次正常枚举的通讯数据,从图中可以看出,WIN10 USB主机在正常获取HID报告描述符后,紧接着会获取虚拟串口状态和设置波特率,这样就正常枚举结束了。我们再来看看采集到的异常USB枚举过程:

捕获2.PNG

Figure 2 异常枚举过程


上图是WIN10下异常枚举过程。从图中可以看出,WIN10系统上USB主机在获取到设备描述符和配置描述符后直接将设备挂起了。很明显,WIN10系统的USB主机驱动实现对设备描述符或者配置描述符的内容并不认可,才会导致无法识别HID+CDC复合设备。

我们不妨检查下客户代码中的设备描述符:

捕获3.PNG

Figure 3 获取的设备描述符


复合设备的class,subclass,protocol

必须为0xef,0x02,0x01,这里

VID=0x0483,PID=0x3256(Cube库下为0x5740,但这个不重要),接下来看配置描述符:

4.jpg

Figure 4 win10不能识别的配置描述符


由此可见,客户的描述符是HID interface + IAD + CDC interfaces结构。对于WIN7,这种结构可以识别,但对于WIN10,这种结构WIN10未必能够兼容,我们尝试在HID interface外部加上一层IAD结构,使其成为IAD1 + HID interface + IAD2 + CDC interfaces结构,此时客户的问题得以解决,在WIN10也可以正确识别了,修改后的描述符结构如下:

5.jpg

Figure 5 win10能够正确识别的配置描述符


结束本篇实战经验之前,让我们再次回顾IAD的概念:

IAD(Interface Association Descriptor),为USB设备定义了一个标准来表述捆绑在一个逻辑功能(比如这里的CDC虚拟串口)上的多个接口的聚合的方法。USB协会分配了一个设备级别的类编码(即图3中0xEF),使用IAD的设备必须使用它(如图3的设备描述符);这样可以很容易在设备枚举时就能识别出采用了IAD的设备。IAD描述符通常放在它所要捆绑的多个接口的接口描述符之前


3 结论

在WIN10系统中,建议复合设备每个逻辑功能的接口描述符前都搭载一个IAD描述符,不论这个逻辑功能是单个接口描述符完成(比如这里的HID功能)还是要由多个接口描述符完成(比如这里的CDC功能)。


本文转自:https://mp.weixin.qq.com/s/9ot9AVTN4zSMAyudVDeMzA

感谢【STM32单片机】分享的经验。

CUBEMX 配置时报引脚冲突,如何解决,这样配置,SPI不能使用,单独配置,SPI与USART均能正常使用

回复

奇缘时间 发起了问题 • 2 人关注 • 0 个回复 • 899 次浏览 • 2018-05-31 09:48 • 来自相关话题

emwin显示汉字

等。。。待 发表了文章 • 0 个评论 • 2886 次浏览 • 2018-04-19 13:49 • 来自相关话题

我们一般用keil编程是,软件的编码模式是GB2312模式,此时emwin显示汉字会显示乱码,无法正常使用。多次实验后,有两种方式可以解决这个问题。第一种:emwin汉字解码用的是UTF-8模式,而我们常用的编码模式是GB2312,二者之间无法使用固定的公式转换,也就是没有直接联系。此时要显示汉字,需要做一个列表,将UTF-8和GB2312两种编码联系起来。    若是将所有的常用汉字列表都写入程序,这个不太现实(文件挺大的),下面是实现方法uint16_t das[23335][2]=//部分列表
{
{0x00,0x0000},
{0x01,0x0001},
{0x02,0x0002},
{0x03,0x0003},
{0x04,0x0004},
{0x05,0x0005},
{0x06,0x0006},
{0x07,0x0007},
{0x08,0x0008},
};
//转换程序
uint16_t Get_GB2312(char *str)
{
uint16_t a=0;
a=(uint8_t)(*str);
a<<=8;
a|=(uint8_t)(*(str+1));
return a;
}

uint16_t GET(char *ds)
{
uint32_t dsa=0;
if(((uint8_t)(*ds))<0x80)
{
return  (uint8_t)(*ds);
}
else  //if (((*ds) &0xf0) && ((*(ds + 1)) &0xf0))
{
dsa=Get_GB2312(ds);
for (i = 0; i < 23335; i++)
{
if (das[i][0] == dsa)
{
return das[i][1];
}
}
}
return 0;
}
const char* GET_UTF1(char *ds)
{

uint8_t *bi1t;
uint16_t dsadd=0;
if(((uint8_t)(*ds))<0x80)
{
return  ds;
}
else  //if (((*ds) &0xf0) && ((*(ds + 1)) &0xf0))
{
dsadd=GET(ds);

bi1t[0] = 0xe0 | ((dsadd >> 12)&0x0f);  //高四位填入1110xxxx 10yyyyyy 10zzzzzz中的xxxx  

bi1t[1] = 0x80 | ((dsadd >> 6) & 0x3f);  //中间6位填入1110xxxx 10yyyyyy 10zzzzzz中的yyyyyy

bi1t[2] = 0x80 | (dsadd & 0x3f);  //低6位填入1110xxxx 10yyyyyy 10zzzzzz中的zzzzzz  
return bi1t;
}
}     再就是只将自己需要的汉字列出来,这样有一个缺点,无法现实任意汉字
 typFNT_GB24 Fontff[100]=
{
{"不","\xe4\xb8\x8d"},
{"同","\xe5\x90\x8c"},
{"期","\xe6\x9c\x9f"},
{"时","\xe6\x97\xb6"},
{"间","\xe9\x97\xb4"},
{"储","\xe5\x82\xa8"},
{"能","\xe8\x83\xbd"},
{"机","\xe6\x9c\xba"},
{"设","\xe8\xae\xbe"},
{"置","\xe7\xbd\xae"},
{"最","\xe6\x9c\x80"},
{"大","\xe5\xa4\xa7"},
{"行","\xe8\xa1\x8c"},
{"程","\xe7\xa8\x8b"},
{"合","\xe5\x90\x88"},
{"分","\xe5\x88\x86"},
{"闸","\xe9\x97\xb8"},
{"电","\xe7\x94\xb5"},
{"流","\xe6\xb5\x81"},
};
uint16_t SearchhzIndex(uint8_t  lo, uint8_t  hi) //查找汉字编码位置
{
    uint16_t i = 0;

for (i = 0; i < sizeof(Fontff); i++)
{
if ((Fontff[i].Index[0] == lo) && (Fontff[i].Index[1] == hi))
{
return i;
}
}
    return 0;
}第二种:将软件编码模式改成UTF-8,这样有一个坏处,写程序的时候会出现莫名其妙的问题 查看全部

我们一般用keil编程是,软件的编码模式是GB2312模式,此时emwin显示汉字会显示乱码,无法正常使用。多次实验后,有两种方式可以解决这个问题。

第一种:emwin汉字解码用的是UTF-8模式,而我们常用的编码模式是GB2312,二者之间无法使用固定的公式转换,也就是没有直接联系。此时要显示汉字,需要做一个列表,将UTF-8和GB2312两种编码联系起来。

    若是将所有的常用汉字列表都写入程序,这个不太现实(文件挺大的),下面是实现方法

uint16_t das[23335][2]=//部分列表
{
{0x00,0x0000},
{0x01,0x0001},
{0x02,0x0002},
{0x03,0x0003},
{0x04,0x0004},
{0x05,0x0005},
{0x06,0x0006},
{0x07,0x0007},
{0x08,0x0008},
};
//转换程序
uint16_t Get_GB2312(char *str)
{
uint16_t a=0;
a=(uint8_t)(*str);
a<<=8;
a|=(uint8_t)(*(str+1));
return a;
}

uint16_t GET(char *ds)
{
uint32_t dsa=0;
if(((uint8_t)(*ds))<0x80)
{
return  (uint8_t)(*ds);
}
else  //if (((*ds) &0xf0) && ((*(ds + 1)) &0xf0))
{
dsa=Get_GB2312(ds);
for (i = 0; i < 23335; i++)
{
if (das[i][0] == dsa)
{
return das[i][1];
}
}
}
return 0;
}
const char* GET_UTF1(char *ds)
{

uint8_t *bi1t;
uint16_t dsadd=0;
if(((uint8_t)(*ds))<0x80)
{
return  ds;
}
else  //if (((*ds) &0xf0) && ((*(ds + 1)) &0xf0))
{
dsadd=GET(ds);

bi1t[0] = 0xe0 | ((dsadd >> 12)&0x0f);  //高四位填入1110xxxx 10yyyyyy 10zzzzzz中的xxxx  

bi1t[1] = 0x80 | ((dsadd >> 6) & 0x3f);  //中间6位填入1110xxxx 10yyyyyy 10zzzzzz中的yyyyyy

bi1t[2] = 0x80 | (dsadd & 0x3f);  //低6位填入1110xxxx 10yyyyyy 10zzzzzz中的zzzzzz  
return bi1t;
}
}

     再就是只将自己需要的汉字列出来,这样有一个缺点,无法现实任意汉字


 typFNT_GB24 Fontff[100]=
{
{"不","\xe4\xb8\x8d"},
{"同","\xe5\x90\x8c"},
{"期","\xe6\x9c\x9f"},
{"时","\xe6\x97\xb6"},
{"间","\xe9\x97\xb4"},
{"储","\xe5\x82\xa8"},
{"能","\xe8\x83\xbd"},
{"机","\xe6\x9c\xba"},
{"设","\xe8\xae\xbe"},
{"置","\xe7\xbd\xae"},
{"最","\xe6\x9c\x80"},
{"大","\xe5\xa4\xa7"},
{"行","\xe8\xa1\x8c"},
{"程","\xe7\xa8\x8b"},
{"合","\xe5\x90\x88"},
{"分","\xe5\x88\x86"},
{"闸","\xe9\x97\xb8"},
{"电","\xe7\x94\xb5"},
{"流","\xe6\xb5\x81"},
};
uint16_t SearchhzIndex(uint8_t  lo, uint8_t  hi) //查找汉字编码位置
{
    uint16_t i = 0;

for (i = 0; i < sizeof(Fontff); i++)
{
if ((Fontff[i].Index[0] == lo) && (Fontff[i].Index[1] == hi))
{
return i;
}
}
    return 0;
}

第二种:将软件编码模式改成UTF-8,这样有一个坏处,写程序的时候会

出现莫名其妙的问题


STM32F103ZET(基于秉火开发板)+Cubemx(F1 V1.60库)+CAN

开始现在-s 发表了文章 • 0 个评论 • 2640 次浏览 • 2017-12-11 11:54 • 来自相关话题

首先说明下开发条件:1、开发板:秉火霸道,STM32F103ZET2、软件:Cubemx V4.23(F1 V1.60库)问题描述: 官方生成的代码没有滤波器设置,需要自己加入解决办法:1、不能直接在cubemx上面设置CAN的引脚,需要自己选择PB9和PB10,然后设为CANTX和RX2、添加滤波器设置在static void MX_CAN_Init(void)函数里面加入滤波器设置函数:    CAN_FilterConfTypeDef  sFilterConfig;  sFilterConfig.FilterNumber = 0;  sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;  sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;  sFilterConfig.FilterIdHigh = 0x0000;  sFilterConfig.FilterIdLow = 0x0000;  sFilterConfig.FilterMaskIdHigh = 0x0000;  sFilterConfig.FilterMaskIdLow = 0x0000;  sFilterConfig.FilterFIFOAssignment = 0; //收到的报文放在FIFO 0里面  sFilterConfig.FilterActivation = ENABLE;  sFilterConfig.BankNumber = 14;  if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)  {    /* Filter configuration Error */    Error_Handler();  }3、联系方式:微信Startingray,本人准备将cubemx的小bug解决的过程记录下来,也是给爱好者一点启示,欢迎微信和我沟通,共同进步!个人觉得Cubemx是未来的发展方向,可以使我们避免很多小错误,但是由于现在软件版本之间的兼容性,加上不同版本库之间存在的bug,还是需要我们在使用时时刻保持警惕! 查看全部

首先说明下开发条件:

1、开发板:秉火霸道,STM32F103ZET

2、软件:Cubemx V4.23(F1 V1.60库)

问题描述: 官方生成的代码没有滤波器设置,需要自己加入


解决办法:

1、不能直接在cubemx上面设置CAN的引脚,需要自己选择PB9和PB10,然后设为CANTX和RX

2、添加滤波器设置


在static void MX_CAN_Init(void)函数里面加入滤波器设置函数:

  

  CAN_FilterConfTypeDef  sFilterConfig;

  sFilterConfig.FilterNumber = 0;

  sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;

  sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;

  sFilterConfig.FilterIdHigh = 0x0000;

  sFilterConfig.FilterIdLow = 0x0000;

  sFilterConfig.FilterMaskIdHigh = 0x0000;

  sFilterConfig.FilterMaskIdLow = 0x0000;

  sFilterConfig.FilterFIFOAssignment = 0; //收到的报文放在FIFO 0里面

  sFilterConfig.FilterActivation = ENABLE;

  sFilterConfig.BankNumber = 14;


  if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)

  {

    /* Filter configuration Error */

    Error_Handler();

  }

3、联系方式:微信Startingray,本人准备将cubemx的小bug解决的过程记录下来,也是给爱好者一点启示,欢迎微信和我沟通,共同进步!个人觉得Cubemx是未来的发展方向,可以使我们避免很多小错误,但是由于现在软件版本之间的兼容性,加上不同版本库之间存在的bug,还是需要我们在使用时时刻保持警惕!

uint32_t SystemCoreClock = 16000000;

回复

wansaiyon 发起了问题 • 1 人关注 • 0 个回复 • 1694 次浏览 • 2017-08-23 23:18 • 来自相关话题

USB 虚拟串口cjons通信

q18378401409 发表了文章 • 0 个评论 • 1041 次浏览 • 2017-07-16 20:21 • 来自相关话题

有两个地方要注意的第一,需要把栈空间改大,我用0x01000第二判断USB接收接收是接收个数小于64。

有两个地方要注意的

第一,需要把栈空间改大,我用0x01000

第二判断USB接收接收是接收个数小于64。

(转)通过后备数据寄存器RTC_BKP0R从用户代码调用系统bootloader来更新应用程序

admin 发表了文章 • 0 个评论 • 2791 次浏览 • 2017-06-16 23:44 • 来自相关话题

我们知道,任何STM32芯片内都包含有一块系统存储器(SystemMemory),里边存储着内部的启动代码Bootloader。不同的STM32型号所支持的用于升级代码的通讯口不尽相同,需要参考应用笔记AN2606。但是,有一个问题避免不了,那就是如何进入System Memory去执行Bootloader?通常的办法都是将BOOT1和BOOT0进行配置:BOOT0拉高,BOOT1拉低(注意:有些型号的BOOT1由选项字节nBOOT1控制)。可是在一些产品中,由于外观的要求,往往不方便在外边开口去放置按键或跳线来改变BOOT脚的电平。而且,用户并不想自己写IAP代码,觉得麻烦。特别是一些产品,需要使用USBDFU来进行代码升级的,而在产品功能中USB又没用到,用户就会觉得自己为了一个通过USB进行代码升级的功能,去写IAP的话,需要去熟悉USB的代码,觉得麻烦,而且这些USB的代码还占用了用户的程序空间。对于这些用户来讲,他们很希望能在不管BOOT脚的情况下能够去调用STM32中System Memory的Bootloader,完成代码升级功能。1.判断其可行性首先,打开应用笔记AN2606《STM32 microcontroller system memory bootmode》,翻到3.1 Bootloader activation一节的最后,可以看到如下信息:这里的意思就是说,用户可以通过从用户代码跳转到系统存储器去执行Bootloader。但是,在跳转到Bootloader之前,有几个事情必须要做好:     1) 关闭所有外设的时钟                            2) 关闭使用的PLL3) 禁用所有中断4) 清除所有挂起的中断标志位最后,去掉Bootloader的激活条件并产生一个硬件复位去执行用户代码,也可以直接使用Go命令去执行用户代码。 那么,如何从用户代码跳转到System Memory中去呢?这个其实并不难,如果写过IAP,或者看过关于IAP的应用笔记中的参考代码的话,比如应用笔记AN3965“STM32F40x/STM32F41x in-application programming using theUSART”及其参考代码STSW-STM32067,都应该知道,IAP的启动代码通过重新设置主堆栈指针并跳转到用户代码来执行用户代码的。同样的道理,只要知道SystemMemory的地址,一样可以从用户代码通过重新设置主堆栈指针并跳转到System Memory来执行Bootloader。而System Memory地址可以从参考手册来获得。比如,查看STM32F411的参考手册RM0383,可以找到如下的表格:那很多人又会问了,我的代码很复杂,用了很多外设,开了很多中断,可是要跳转到System Memory中的Bootloader,需要关所有外设的时钟,需要关PLL,需要关闭所有中断,需要禁用所有的中断,清除所有挂起的中断。这可是一项非常庞大的的任务啊!所以,在这里,我们需要一个更简单的事情来完成这项庞大的任务。其实真的就有这么简单的一个方法——复位!通过软件复位来实现这一目的。但是,复位后,又怎么知道还记得我们要去做代码升级呢?这又要用到STM32另一个特性了,那就是后备数据寄存器Backup Data Registers在软件复位后会保留其值,这样给了我们在复位前后做一个标志的机会。这样,考证下来,客户的需求是具备可行性的。接下来需要做的是理清思路。2.软件流程这里使用32F411EDISCOVERY板来设计一个参考例程:设计一个用户程序,让LED3进行闪烁;当用户按键被按下,产生EXTI中断,在中断中选择后备数据寄存器RTC_BKP0R,写入值0x32F2,然后产生软件复位;软件复位后,在运行代码的最前面对RTC_BKP0R进行判断,如果其值不是0x32F2则直接去运行用户代码,如果其值为0x32F2则是需要跳转到Bootloader去进行代码升级,并在跳转前将RTC_BKP0R清零。这样,在进入Bootloader后,客户进行USB DFU升级后,将来不会因为不需要升级代码的复位而误入Bootloader。 来看软件流程图,先来看主程序的流程图:3.主要代码使用STM32F4Cube库来开发这个例程。先来看位于main.c中的main函数:int main(void)
{
  /* STM32F4xx HALlibrary initialization:
- Configure theFlash prefetch, Flash preread and Buffer caches
- Systick timer isconfigured by default as source of time base, but user can eventually implement his proper time base source (a general purpose timer for example or other time source), keeping in mind that Time base durationshould be kept 1ms since PPP_TIMEOUT_VALUEs are defined and handled inmilliseconds basis.
 - Low LevelInitialization     */

  HAL_Init();
 
  /* Configure the Systemclock to have a frequency of 100 MHz */
  SystemClock_Config();
 
  /* Configure LED3,LED4, LED5 and LED6 */
  BSP_LED_Init(LED3);
  BSP_LED_Init(LED4);
  BSP_LED_Init(LED5);
  BSP_LED_Init(LED6); 
 
  /* Configure EXTI Line0(connected to PA0 pin) in interrupt mode */
  EXTILine0_Config();
 
  /* Infinite loop */
  while (1)
  {
  }
}Main函数很简单,配置系统时钟,对使用的LED进行初始化,然后配置了用户按键的EXTI中断,然后就进入主循环了。前面说到,要实现用户的功能程序为LED3闪烁,在主循环我们没看到,是因为在Cube库中,会使用SysTick,所以把LED3的闪烁放到SysTick的中断代码中了,查看stm32f4xx_it.c,如下:void SysTick_Handler(void)
{
  HAL_IncTick();
 
  // LED3 Toggle
  led_toggle_counter++;
  if (led_toggle_counter>= 500)
  {
    led_toggle_counter =0;
    BSP_LED_Toggle(LED3);
  }
}从main函数最开始的那段注释中知道,跳入main函数前,在startup_stm32f411xe.s中早已经先调用执行了位于system_stm32f4xx.c中的SystemInit函数。SystemInit函数主要执行初始化FPU、复位RCC时钟寄存器、配置向量表等功能。由于我们希望在最原始的状态下进入SystemMemory,所以我们将跳转到System Memory放在这个函数的最前头,如下:void SystemInit(void)
{
  /* Check if need to gointo bootloader before configure clock*/
  RtcHandle.Instance =RTC; 
 if(HAL_RTCEx_BKUPRead(&RtcHandle, RTC_BKP_DR0) ==0x32F2)
  {
   __HAL_RCC_PWR_CLK_ENABLE();
   HAL_PWR_EnableBkUpAccess();
   __HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_HSE_DIV2);
   __HAL_RCC_RTC_ENABLE();
   HAL_RTCEx_BKUPWrite(&RtcHandle, RTC_BKP_DR0,0x0);
   __HAL_RCC_RTC_DISABLE();
    HAL_PWR_DisableBkUpAccess();
   __HAL_RCC_PWR_CLK_DISABLE();
   
   __set_MSP(*(__IO uint32_t*) 0x1FFF0000);
   SysMemBootJump = (void (*)(void)) (*((uint32_t *) 0x1FFF0004));
   SysMemBootJump();
    while (1);
  }
 
  /* FPU settings------------------------------------------------------------*/
  #if (__FPU_PRESENT ==1) && (__FPU_USED == 1)
    SCB->CPACR |=((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
  #endif
  /* Reset the RCC clockconfiguration to the default reset state ------------*/
  /* Set HSION bit */
  RCC->CR |=(uint32_t)0x00000001;
 
  /* Reset CFGR register*/
  RCC->CFGR =0x00000000;
 
  /* Reset HSEON, CSSONand PLLON bits */
  RCC->CR &=(uint32_t)0xFEF6FFFF;
 
  /* Reset PLLCFGRregister */
  RCC->PLLCFGR =0x24003010;
 
  /* Reset HSEBYP bit */
  RCC->CR &=(uint32_t)0xFFFBFFFF;
 
  /* Disable allinterrupts */
  RCC->CIR =0x00000000;
 
  /* Configure the VectorTable location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
  SCB->VTOR =SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
  SCB->VTOR =FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
}可以看到,在函数的最前面对RTC_BKP_DR0进行了判断,如果其值为0x32F2的话,则先启动备份域的写入时序,如RM0383中5.1.2 Battery backup domain所描述的:然后将RTC_BKP_DR0清零,再关闭执行这次操作所打开的时钟。主堆栈指针MSP的初始值位于向量表偏移量为0x00的位置,复位Reset的值则位于向量表偏移量为0x04的位置。对于STM32F411来说,当执行System Memeory中的Bootloader时,MSP的初始值位于0x1FFF0000,而Reset向量则位于0x1FFF0004。所以在程序中,使用__set_MSP(*(__IO uint32_t*) 0x1FFF0000);来重新设置主堆栈指针,而后再跳转到0x1FFF0004所指向的程序地址去执行Bootloader。 再来看位于stm32f4xx_it.c中的EXTI中断程序:void EXTI0_IRQHandler(void)
{
 HAL_GPIO_EXTI_IRQHandler(KEY_BUTTON_PIN);
}
及其位于main.c中的Callback函数:
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if(GPIO_Pin ==KEY_BUTTON_PIN)
  {
   __HAL_RCC_PWR_CLK_ENABLE();
   HAL_PWR_EnableBkUpAccess();
   __HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_HSE_DIV2);
   __HAL_RCC_RTC_ENABLE();
    RtcHandle.Instance =RTC;
   HAL_RTCEx_BKUPWrite(&RtcHandle, RTC_BKP_DR0,0x32F2);
   if(HAL_RTCEx_BKUPRead(&RtcHandle, RTC_BKP_DR0) != 0x32F2)
    {
      // Write backupdata memory failed
      BSP_LED_On(LED5);
      while (1) ;               // Error
    }
    else
    {
      // Write backupdata memory succeeded.
      BSP_LED_On(LED6);
      HAL_NVIC_SystemReset();   // Software reset for going into bootloader
      while (1)   ;
    }
  }
}当判断到用户按键按下,需要进行用户代码升级时,先启动备份域的访问时序,将RTC_BKP_DR0的值写为0x32F2。再读回来判断是否写入成功,以方便调试。如果写入成功后,则就调用HAL_NVIC_SystemReset()进行软件复位。重新复位后,就可以进入System Memory了。原帖地址:http://mp.weixin.qq.com/s/WptYinG-5pCjLt2x7E20SA 查看全部

我们知道,任何STM32芯片内都包含有一块系统存储器(SystemMemory),里边存储着内部的启动代码Bootloader。不同的STM32型号所支持的用于升级代码的通讯口不尽相同,需要参考应用笔记AN2606。

但是,有一个问题避免不了,那就是如何进入System Memory去执行Bootloader?

通常的办法都是将BOOT1和BOOT0进行配置:BOOT0拉高,BOOT1拉低(注意:有些型号的BOOT1由选项字节nBOOT1控制)。

可是在一些产品中,由于外观的要求,往往不方便在外边开口去放置按键或跳线来改变BOOT脚的电平。

而且,用户并不想自己写IAP代码,觉得麻烦。特别是一些产品,需要使用USBDFU来进行代码升级的,而在产品功能中USB又没用到,用户就会觉得自己为了一个通过USB进行代码升级的功能,去写IAP的话,需要去熟悉USB的代码,觉得麻烦,而且这些USB的代码还占用了用户的程序空间。对于这些用户来讲,他们很希望能在不管BOOT脚的情况下能够去调用STM32中System Memory的Bootloader,完成代码升级功能。


1.判断其可行性

首先,打开应用笔记AN2606《STM32 microcontroller system memory bootmode》,翻到3.1 Bootloader activation一节的最后,可以看到如下信息:

blob.png


这里的意思就是说,用户可以通过从用户代码跳转到系统存储器去执行Bootloader。但是,在跳转到Bootloader之前,有几个事情必须要做好:

     1) 关闭所有外设的时钟                            

2) 关闭使用的PLL

3) 禁用所有中断

4) 清除所有挂起的中断标志位

最后,去掉Bootloader的激活条件并产生一个硬件复位去执行用户代码,也可以直接使用Go命令去执行用户代码。

 

那么,如何从用户代码跳转到System Memory中去呢?这个其实并不难,如果写过IAP,或者看过关于IAP的应用笔记中的参考代码的话,比如应用笔记AN3965“STM32F40x/STM32F41x in-application programming using theUSART”及其参考代码STSW-STM32067,都应该知道,IAP的启动代码通过重新设置主堆栈指针并跳转到用户代码来执行用户代码的。同样的道理,只要知道SystemMemory的地址,一样可以从用户代码通过重新设置主堆栈指针并跳转到System Memory来执行Bootloader。而System Memory地址可以从参考手册来获得。比如,查看STM32F411的参考手册RM0383,可以找到如下的表格:

blob.png

那很多人又会问了,我的代码很复杂,用了很多外设,开了很多中断,可是要跳转到System Memory中的Bootloader,需要关所有外设的时钟,需要关PLL,需要关闭所有中断,需要禁用所有的中断,清除所有挂起的中断。这可是一项非常庞大的的任务啊!所以,在这里,我们需要一个更简单的事情来完成这项庞大的任务。其实真的就有这么简单的一个方法——复位!通过软件复位来实现这一目的。但是,复位后,又怎么知道还记得我们要去做代码升级呢?这又要用到STM32另一个特性了,那就是后备数据寄存器Backup Data Registers在软件复位后会保留其值,这样给了我们在复位前后做一个标志的机会。

这样,考证下来,客户的需求是具备可行性的。接下来需要做的是理清思路。


2.软件流程

这里使用32F411EDISCOVERY板来设计一个参考例程:设计一个用户程序,让LED3进行闪烁;当用户按键被按下,产生EXTI中断,在中断中选择后备数据寄存器RTC_BKP0R,写入值0x32F2,然后产生软件复位;软件复位后,在运行代码的最前面对RTC_BKP0R进行判断,如果其值不是0x32F2则直接去运行用户代码,如果其值为0x32F2则是需要跳转到Bootloader去进行代码升级,并在跳转前将RTC_BKP0R清零。这样,在进入Bootloader后,客户进行USB DFU升级后,将来不会因为不需要升级代码的复位而误入Bootloader。

 

来看软件流程图,先来看主程序的流程图:

blob.png

blob.png

3.主要代码

使用STM32F4Cube库来开发这个例程。先来看位于main.c中的main函数:

int main(void)
{
  /* STM32F4xx HALlibrary initialization:
- Configure theFlash prefetch, Flash preread and Buffer caches
- Systick timer isconfigured by default as source of time base, but user can eventually implement his proper time base source (a general purpose timer for example or other time source), keeping in mind that Time base durationshould be kept 1ms since PPP_TIMEOUT_VALUEs are defined and handled inmilliseconds basis.
 - Low LevelInitialization     */

  HAL_Init();
 
  /* Configure the Systemclock to have a frequency of 100 MHz */
  SystemClock_Config();
 
  /* Configure LED3,LED4, LED5 and LED6 */
  BSP_LED_Init(LED3);
  BSP_LED_Init(LED4);
  BSP_LED_Init(LED5);
  BSP_LED_Init(LED6); 
 
  /* Configure EXTI Line0(connected to PA0 pin) in interrupt mode */
  EXTILine0_Config();
 
  /* Infinite loop */
  while (1)
  {
  }
}

Main函数很简单,配置系统时钟,对使用的LED进行初始化,然后配置了用户按键的EXTI中断,然后就进入主循环了。前面说到,要实现用户的功能程序为LED3闪烁,在主循环我们没看到,是因为在Cube库中,会使用SysTick,所以把LED3的闪烁放到SysTick的中断代码中了,查看stm32f4xx_it.c,如下:

void SysTick_Handler(void)
{
  HAL_IncTick();
 
  // LED3 Toggle
  led_toggle_counter++;
  if (led_toggle_counter>= 500)
  {
    led_toggle_counter =0;
    BSP_LED_Toggle(LED3);
  }
}

从main函数最开始的那段注释中知道,跳入main函数前,在startup_stm32f411xe.s中早已经先调用执行了位于system_stm32f4xx.c中的SystemInit函数。SystemInit函数主要执行初始化FPU、复位RCC时钟寄存器、配置向量表等功能。由于我们希望在最原始的状态下进入SystemMemory,所以我们将跳转到System Memory放在这个函数的最前头,如下:

void SystemInit(void)
{
  /* Check if need to gointo bootloader before configure clock*/
  RtcHandle.Instance =RTC; 
 if(HAL_RTCEx_BKUPRead(&RtcHandle, RTC_BKP_DR0) ==0x32F2)
  {
   __HAL_RCC_PWR_CLK_ENABLE();
   HAL_PWR_EnableBkUpAccess();
   __HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_HSE_DIV2);
   __HAL_RCC_RTC_ENABLE();
   HAL_RTCEx_BKUPWrite(&RtcHandle, RTC_BKP_DR0,0x0);
   __HAL_RCC_RTC_DISABLE();
    HAL_PWR_DisableBkUpAccess();
   __HAL_RCC_PWR_CLK_DISABLE();
   
   __set_MSP(*(__IO uint32_t*) 0x1FFF0000);
   SysMemBootJump = (void (*)(void)) (*((uint32_t *) 0x1FFF0004));
   SysMemBootJump();
    while (1);
  }
 
  /* FPU settings------------------------------------------------------------*/
  #if (__FPU_PRESENT ==1) && (__FPU_USED == 1)
    SCB->CPACR |=((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
  #endif
  /* Reset the RCC clockconfiguration to the default reset state ------------*/
  /* Set HSION bit */
  RCC->CR |=(uint32_t)0x00000001;
 
  /* Reset CFGR register*/
  RCC->CFGR =0x00000000;
 
  /* Reset HSEON, CSSONand PLLON bits */
  RCC->CR &=(uint32_t)0xFEF6FFFF;
 
  /* Reset PLLCFGRregister */
  RCC->PLLCFGR =0x24003010;
 
  /* Reset HSEBYP bit */
  RCC->CR &=(uint32_t)0xFFFBFFFF;
 
  /* Disable allinterrupts */
  RCC->CIR =0x00000000;
 
  /* Configure the VectorTable location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
  SCB->VTOR =SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
  SCB->VTOR =FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
}

可以看到,在函数的最前面对RTC_BKP_DR0进行了判断,如果其值为0x32F2的话,则先启动备份域的写入时序,如RM0383中5.1.2 Battery backup domain所描述的:

blob.png

然后将RTC_BKP_DR0清零,再关闭执行这次操作所打开的时钟。

主堆栈指针MSP的初始值位于向量表偏移量为0x00的位置,复位Reset的值则位于向量表偏移量为0x04的位置。对于STM32F411来说,当执行System Memeory中的Bootloader时,MSP的初始值位于0x1FFF0000,而Reset向量则位于0x1FFF0004。所以在程序中,使用__set_MSP(*(__IO uint32_t*) 0x1FFF0000);来重新设置主堆栈指针,而后再跳转到0x1FFF0004所指向的程序地址去执行Bootloader。

 

再来看位于stm32f4xx_it.c中的EXTI中断程序:

void EXTI0_IRQHandler(void)
{
 HAL_GPIO_EXTI_IRQHandler(KEY_BUTTON_PIN);
}
及其位于main.c中的Callback函数:
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if(GPIO_Pin ==KEY_BUTTON_PIN)
  {
   __HAL_RCC_PWR_CLK_ENABLE();
   HAL_PWR_EnableBkUpAccess();
   __HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_HSE_DIV2);
   __HAL_RCC_RTC_ENABLE();
    RtcHandle.Instance =RTC;
   HAL_RTCEx_BKUPWrite(&RtcHandle, RTC_BKP_DR0,0x32F2);
   if(HAL_RTCEx_BKUPRead(&RtcHandle, RTC_BKP_DR0) != 0x32F2)
    {
      // Write backupdata memory failed
      BSP_LED_On(LED5);
      while (1) ;               // Error
    }
    else
    {
      // Write backupdata memory succeeded.
      BSP_LED_On(LED6);
      HAL_NVIC_SystemReset();   // Software reset for going into bootloader
      while (1)   ;
    }
  }
}

当判断到用户按键按下,需要进行用户代码升级时,先启动备份域的访问时序,将RTC_BKP_DR0的值写为0x32F2。再读回来判断是否写入成功,以方便调试。如果写入成功后,则就调用HAL_NVIC_SystemReset()进行软件复位。重新复位后,就可以进入System Memory了。


原帖地址:http://mp.weixin.qq.com/s/WptYinG-5pCjLt2x7E20SA


(转)在转一篇针对u8glib单色屏库+SSD1306 的使用介绍

admin 发表了文章 • 0 个评论 • 2034 次浏览 • 2016-07-04 11:49 • 来自相关话题

Using the u8glib on the STM32
The u8glib is a really nice library to use when you don’t want to mess around with different diplay protocols or drawing routines. It is well-conceived and easy to use. Unfortunately there has been no port for STM32 microcontrollers so I tried to do this.

In the following example I will use the library to display some text on a SSD1306 display via I2C. The controller I use is the STM32F101CBT6 and the IDE will be Atollic True Studio for ARM Lite.

You should start by downloading the u8glib for arm. This is the C version of the library which I will use since you can only compile C code with the Lite version of my IDE.

The next step is to implement the entire project into your code. So I created a new folder and just draged in the „src“ folder from the archive.

Now some low level routines have to be implemented to allow the library to talk to the display device. The following routines have to be created:
Delay by n microsecondsDelay by 10 microsecondsDelay by 1 microsecondDoing a Reset on the displayWriting a byte to the displayWriting a sequence of bytes to the displaySwitching from data to command mode or vice versa
To do so we create a new source file called „u8g_arm.c“ and a header file „u8g_arm.h„.

In the header file you should include the original u8g.h header file so basically just do:
#ifndef _U8G_ARM_H
#define _U8G_ARM_H

//adjust this path:
#include "u8glib/u8g.h"

//main com function. read on...
uint8_t u8g_com_hw_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr);

#endifThe most important procedure in these files will be the one that is called by the u8g library with data to send to the display device. This communication procedure looks like this. I added comments for every important statement. Add it to the file „u8g_arm.c“.
uint8_t u8g_com_hw_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
{

switch(msg)
{
case U8G_COM_MSG_STOP:
//STOP THE DEVICE
break;

case U8G_COM_MSG_INIT:
//INIT HARDWARE INTERFACES, TIMERS, GPIOS...
break;

case U8G_COM_MSG_ADDRESS:
//SWITCH FROM DATA TO COMMAND MODE (arg_val == 0 for command mode)
break;

case U8G_COM_MSG_RESET:
//TOGGLE THE RESET PIN ON THE DISPLAY BY THE VALUE IN arg_val
break;

case U8G_COM_MSG_WRITE_BYTE:
//WRITE BYTE TO DEVICE
break;

case U8G_COM_MSG_WRITE_SEQ:
case U8G_COM_MSG_WRITE_SEQ_P:
//WRITE A SEQUENCE OF BYTES TO THE DEVICE
break;

}
return 1;
}Now just add code to every case of the switch statement. For my communication with I2C it looks like this:
uint8_t u8g_com_hw_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
{

switch(msg)
{
case U8G_COM_MSG_STOP:
break; //do nothing...

case U8G_COM_MSG_INIT:
i2c_init(); //inits the i2c hardware, gpios and timers
u8g_MicroDelay();
break;

case U8G_COM_MSG_ADDRESS:
//the control byte switches the mode on the device and is set here
if (arg_val == 0)
{
control = 0;
}
else
{
control = 0x40;
}
u8g_10MicroDelay();
break;

case U8G_COM_MSG_RESET:
//pin 9 is my reset pin
GPIO_WriteBit(GPIOB, GPIO_Pin_9, arg_val);
u8g_10MicroDelay();
break;

case U8G_COM_MSG_WRITE_BYTE:
//simple: just write one byte
i2c_out(arg_val);
u8g_MicroDelay();
break;

case U8G_COM_MSG_WRITE_SEQ:
case U8G_COM_MSG_WRITE_SEQ_P:
{
register uint8_t *ptr = arg_ptr;
//send the control byte (to switch from command to data mode)
I2C_start(SSD1306_I2C_ADDRESS, I2C_Direction_Transmitter);
I2C_SendData(I2C2, control);
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

//now send the rest of data
while( arg_val > 0 )
{
I2C_SendData(I2C2, *ptr++);
arg_val--;
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
}
//done
I2C_stop();
u8g_MicroDelay();
}
break;

}
return 1;
}My routines to  write and read data via I2C are basically copied from this nice article. If you want the full code please contact me or write a comment since I don’t want to publish it because of bad style.

Ok so now we have the low level hardware communication set up. Now it is time to really enjoy the amazing u8g library.

To tell u8glib to use our communication function we just have to call it like this.
My routines to write and read data via I2C are basically copied from this nice article. If you want the full code please contact me or write a comment since I don’t want to publish it because of bad style.

Ok so now we have the low level hardware communication set up. Now it is time to really enjoy the amazing u8g library.

To tell u8glib to use our communication function we just have to call it like this.I hope you noticed that the third parameter to this function is our hardware routine.

That is basically all you have to do. Now you can use all functions of the u8glib. Please remind that you use the C library of the project so you cannot directly copy code from the tutorials.
 
>>引用自:http://blog.bastelhalde.de/?p=759 查看全部
Using the u8glib on the STM32
The u8glib is a really nice library to use when you don’t want to mess around with different diplay protocols or drawing routines. It is well-conceived and easy to use. Unfortunately there has been no port for STM32 microcontrollers so I tried to do this.

In the following example I will use the library to display some text on a SSD1306 display via I2C. The controller I use is the STM32F101CBT6 and the IDE will be Atollic True Studio for ARM Lite.

You should start by downloading the u8glib for arm. This is the C version of the library which I will use since you can only compile C code with the Lite version of my IDE.

The next step is to implement the entire project into your code. So I created a new folder and just draged in the „src“ folder from the archive.

Now some low level routines have to be implemented to allow the library to talk to the display device. The following routines have to be created:
  • Delay by n microseconds
  • Delay by 10 microseconds
  • Delay by 1 microsecond
  • Doing a Reset on the display
  • Writing a byte to the display
  • Writing a sequence of bytes to the display
  • Switching from data to command mode or vice versa

To do so we create a new source file called „u8g_arm.c“ and a header file „u8g_arm.h„.

In the header file you should include the original u8g.h header file so basically just do:
#ifndef _U8G_ARM_H
#define _U8G_ARM_H

//adjust this path:
#include "u8glib/u8g.h"

//main com function. read on...
uint8_t u8g_com_hw_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr);

#endif
The most important procedure in these files will be the one that is called by the u8g library with data to send to the display device. This communication procedure looks like this. I added comments for every important statement. Add it to the file „u8g_arm.c“.
uint8_t u8g_com_hw_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
{

switch(msg)
{
case U8G_COM_MSG_STOP:
//STOP THE DEVICE
break;

case U8G_COM_MSG_INIT:
//INIT HARDWARE INTERFACES, TIMERS, GPIOS...
break;

case U8G_COM_MSG_ADDRESS:
//SWITCH FROM DATA TO COMMAND MODE (arg_val == 0 for command mode)
break;

case U8G_COM_MSG_RESET:
//TOGGLE THE RESET PIN ON THE DISPLAY BY THE VALUE IN arg_val
break;

case U8G_COM_MSG_WRITE_BYTE:
//WRITE BYTE TO DEVICE
break;

case U8G_COM_MSG_WRITE_SEQ:
case U8G_COM_MSG_WRITE_SEQ_P:
//WRITE A SEQUENCE OF BYTES TO THE DEVICE
break;

}
return 1;
}
Now just add code to every case of the switch statement. For my communication with I2C it looks like this:
uint8_t u8g_com_hw_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
{

switch(msg)
{
case U8G_COM_MSG_STOP:
break; //do nothing...

case U8G_COM_MSG_INIT:
i2c_init(); //inits the i2c hardware, gpios and timers
u8g_MicroDelay();
break;

case U8G_COM_MSG_ADDRESS:
//the control byte switches the mode on the device and is set here
if (arg_val == 0)
{
control = 0;
}
else
{
control = 0x40;
}
u8g_10MicroDelay();
break;

case U8G_COM_MSG_RESET:
//pin 9 is my reset pin
GPIO_WriteBit(GPIOB, GPIO_Pin_9, arg_val);
u8g_10MicroDelay();
break;

case U8G_COM_MSG_WRITE_BYTE:
//simple: just write one byte
i2c_out(arg_val);
u8g_MicroDelay();
break;

case U8G_COM_MSG_WRITE_SEQ:
case U8G_COM_MSG_WRITE_SEQ_P:
{
register uint8_t *ptr = arg_ptr;
//send the control byte (to switch from command to data mode)
I2C_start(SSD1306_I2C_ADDRESS, I2C_Direction_Transmitter);
I2C_SendData(I2C2, control);
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

//now send the rest of data
while( arg_val > 0 )
{
I2C_SendData(I2C2, *ptr++);
arg_val--;
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
}
//done
I2C_stop();
u8g_MicroDelay();
}
break;

}
return 1;
}
My routines to  write and read data via I2C are basically copied from this nice article. If you want the full code please contact me or write a comment since I don’t want to publish it because of bad style.

Ok so now we have the low level hardware communication set up. Now it is time to really enjoy the amazing u8g library.

To tell u8glib to use our communication function we just have to call it like this.
My routines to  write and read data via I2C are basically copied from this nice article. If you want the full code please contact me or write a comment since I don’t want to publish it because of bad style.

Ok so now we have the low level hardware communication set up. Now it is time to really enjoy the amazing u8g library.

To tell u8glib to use our communication function we just have to call it like this.
I hope you noticed that the third parameter to this function is our hardware routine.

That is basically all you have to do. Now you can use all functions of the u8glib. Please remind that you use the C library of the project so you cannot directly copy code from the tutorials.
 
>>引用自:http://blog.bastelhalde.de/?p=759

(转)C笔记之enum枚举类型的使用

回复

admin 发起了问题 • 2 人关注 • 0 个回复 • 2043 次浏览 • 2016-06-15 11:04 • 来自相关话题

warning: #550-D: variable "xFrameReceived" was set but never used

回复

myuse 发起了问题 • 1 人关注 • 0 个回复 • 1524 次浏览 • 2016-05-27 22:15 • 来自相关话题

分享一个多路ADC循环扫描+DMA传输的例子

回复

DengQilong 发起了问题 • 1 人关注 • 0 个回复 • 1824 次浏览 • 2016-03-19 12:24 • 来自相关话题