为了制作一个小巧低功耗的温湿度计,特选取了一款迷你型的开发板STM32C0116_DK,其外观见图1和图2所示,由此可知它是采用双面器件布局,只是背面的器件比较少。

图1 正面布局

图2 背面布局
在完成程序下载的情况下,单是一个20PIN的直插式管座就可容下其核心板的体量。再配上蓝牙透传模块及温湿度传感器,其尺寸也大不到哪去。
本以为只用一个GPIO引脚外加2个串口引脚就可以解决问题,然而理想很丰满,现实很骨感,当找到相关例程才发现,竟连个串口的例程都没通过,见图3所示。

图3 例程内容
无奈之下,只好自行探索来解决啦,并尽可能将其潜在的性能发掘出来。
既然可利用的资源不多,就索性从GPIO入手,逐步趟出一些门道来。
1. 彩灯效果的实现
在例程中提供了板载LED的示例程序,参照它的使用方法,不难掌握其引脚的配置方法及输出电平的控制。
由于该开发板在引脚的使用上没有采用连续排列的方式,而是若干个引脚分为一组来排列,因此较耗费排针。

图4 引脚排列关系
为此,是使用3个相对接近的引脚来连接RGB_LED模块,具体的连接关系为:
R----PC15
G----PA7
B----PB7

图5 器件连接
相应的引脚配置函数为:
void RGB_config(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Pin = GPIO_PIN_7;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_7;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
__HAL_RCC_GPIOC_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_15;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}实现彩灯控制效果的主程序为:
int main(void)
{
HAL_Init();
SystemClock_Config();
LED3_GPIO_CLK_ENABLE();
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Pin = LED3_PIN;
HAL_GPIO_Init(LED3_GPIO_PORT, &GPIO_InitStruct);
RGB_config();
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_RESET);
while (1)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_15); //R
HAL_Delay(500);
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_15);
HAL_Delay(500);
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_7); //G
HAL_Delay(500);
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_7);
HAL_Delay(500);
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7); //B
HAL_Delay(500);
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET); //Y
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_SET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_RESET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_SET); //P
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET); //BL
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
HAL_Delay(500);
}
}经程序的编译下载,其测试效果见图6至图11所示,说明GPIO的使用成功。

图6 点亮红灯

图7 点亮绿灯

图8 点亮蓝灯

图9 粉色效果

图10 黄色效果

图11 浅蓝色效果
比较有意思的是,开发板既支持传统的调试器下载方式,也支持将目标文件拖如开发板虚拟U盘的方式。

图12 调试器连接状态

图13 虚拟U盘
2.OLED屏现实驱动
有了GPIO的使用经验,就不难以模拟的方式来驱动I2C接口的OLED屏,以便于显示信息。
OLED屏与开发板的连接关系为:
SCK----PA7
SDA----PC15
模拟I2C方式方式字节数据的函数为:
void Write_IIC_Byte(unsigned char IIC_Byte)
{
unsigned char i;
unsigned char m,da;
da=IIC_Byte;
OLED_SCLK_Clr();
for(i=0;i<8;i++)
{
m=da;
m=m&0x80;
if(m==0x80)
{
OLED_SDIN_Set();
}
else OLED_SDIN_Clr();
delay_us(3);
da=da<<1;
OLED_SCLK_Set();
delay_us(3);
OLED_SCLK_Clr();
delay_us(3);
}
}OLED屏的初始化函数为;
void OLED_Init(void)
{
Write_IIC_Command(0xAE);
Write_IIC_Command(0x20);
Write_IIC_Command(0x10);
Write_IIC_Command(0xb0);
Write_IIC_Command(0xc8);
Write_IIC_Command(0x00);
Write_IIC_Command(0x10);
Write_IIC_Command(0x40);
Write_IIC_Command(0x81);
Write_IIC_Command(0xdf);
Write_IIC_Command(0xa1);
Write_IIC_Command(0xa6);
Write_IIC_Command(0xa8);
Write_IIC_Command(0x3F);
Write_IIC_Command(0xa4);
Write_IIC_Command(0xd3);
Write_IIC_Command(0x00);
Write_IIC_Command(0xd5);
Write_IIC_Command(0xf0);
Write_IIC_Command(0xd9);
Write_IIC_Command(0x22);
Write_IIC_Command(0xda);
Write_IIC_Command(0x12);
Write_IIC_Command(0xdb);
Write_IIC_Command(0x20);
Write_IIC_Command(0x8d);
Write_IIC_Command(0x14);
Write_IIC_Command(0xaf);l
}OLED屏的清屏函数为:
void OLED_Clear(void)
{
uint8_t i,n;
for(i=0;i<8;i++)
{
OLED_WR_Byte (0xb0+i,OLED_CMD);
OLED_WR_Byte (0x00,OLED_CMD);
OLED_WR_Byte (0x10,OLED_CMD);
for(n=0;n<128;n++)OLED_WR_Byte(0x0,OLED_DATA);
}
}显示字符的函数为:
void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t Char_Size)
{
unsigned char c=0,i=0;
c=chr-' ';
if(x>Max_Column-1){x=0;y=y+2;}
if(Char_Size ==16)
{
OLED_Set_Pos(x,y);
for(i=0;i<8;i++)
OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);
OLED_Set_Pos(x,y+1);
for(i=0;i<8;i++)
OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);
}
else
{
OLED_Set_Pos(x,y);
for(i=0;i<6;i++)
OLED_WR_Byte(F6x8[c][i],OLED_DATA);
}
}显示数值的函数为:
void OLED_ShowNum(uint8_t x,uint8_t y,uint32_t num,uint8_t len,uint8_t size2)
{
uint8_t t,temp;
uint8_t enshow=0;
for(t=0;t<len;t++)
{
temp=(num/oled_pow(10,len-t-1))%10;
if(enshow==0&&t<(len-1))
{
if(temp==0)
{
OLED_ShowChar(x+(size2/2)*t,y,' ',size2);
continue;
}else enshow=1;
}
OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2);
}
}显示字符串的函数为:
void OLED_ShowString(uint8_t x,uint8_t y,uint8_t *chr,uint8_t Char_Size)
{
unsigned char j=0;
while (chr[j]!='\0')
{
OLED_ShowChar(x,y,chr[j],Char_Size);
x+=8;
if(x>120) {x=0;y+=2;}
j++;
}
}测试OLED屏显示效果的主程序为:
int main(void)
{
HAL_Init();
SystemClock_Config();
OLED_config();
OLED_Init();
OLED_Clear();
OLED_ShowString(0,2," STM32C0116_DK",16);
while (1);
}经测试,其显示效果见图14所示,说明驱动成功。后面的测试,就可以凭他来观察信息结果啦。

图14 显示效果
3.RTC电子时钟的实现
有了OLED屏的显示功能,就可验证RTC的计时功能,并实现电子时钟功能。
RTC的初始化函数为:
static void MX_RTC_Init(void)
{
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
hrtc.Init.AsynchPrediv = RTC_ASYNCH_PREDIV;
hrtc.Init.SynchPrediv = RTC_SYNCH_PREDIV;
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
hrtc.Init.OutPutPullUp = RTC_OUTPUT_PULLUP_NONE;
if (HAL_RTC_Init(&hrtc) != HAL_OK)
{
Error_Handler();
}
if (__HAL_RTC_GET_FLAG(&hrtc, RTC_FLAG_INITS) == 0x00u)
{
sTime.Hours = 0x2;
sTime.Minutes = 0x0;
sTime.Seconds = 0x0;
sTime.SubSeconds = 0x0;
sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime.StoreOperation = RTC_STOREOPERATION_RESET;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
sDate.WeekDay = RTC_WEEKDAY_MONDAY;
sDate.Month = RTC_MONTH_DECEMBER;
sDate.Date = 0x6;
sDate.Year = 0x21;
if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
}
}显示RTC计时值的函数为:
static void RTC_CalendarShow(uint8_t *showtime, uint8_t *showdate)
{
RTC_DateTypeDef sdatestructureget;
RTC_TimeTypeDef stimestructureget;
HAL_RTC_GetTime(&hrtc, &stimestructureget, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &sdatestructureget, RTC_FORMAT_BIN);
sprintf((char *)showtime, "%2d:%2d:%2d", stimestructureget.Hours, stimestructureget.Minutes, stimestructureget.Seconds);
OLED_ShowString(8,4,showtime,16);
}实现电子时钟功能的测试主程序为:
int main(void)
{
HAL_Init();
BSP_LED_Init(LED3);
MX_GPIO_Init();
SystemClock_Config();
OLED_config();
OLED_Init();
OLED_Clear();
OLED_ShowString(0,1," STM32C0116_DK",16);
MX_RTC_Init();
RTCStatus = 1;
while (1)
{
RTC_CalendarShow(aShowTime, aShowDate);
}
}经实际测试,其显示效果如图15所示,说明功能正确。

图15 计时效果
至此,就实现了电子时钟功能,后续再解决A/D数据采集和串口通信等问题。
我要赚赏金
