概述
电子广告牌多用于显示一些简短的信息,像高铁显示行程等数据。在门市上常用于循环显示广告等功能,这次就使用STM32快速开发一个电子广告牌来帮助学长毕业啦!此电子广告牌显示的内容为:
(喜迎23级新生到校->2023年?月?日 当前天气气温?度湿度?%RH-> 恭喜19级学生顺利毕业!->2023年?月?日 当前天气气温?度湿度?%RH)
然后一直循环…
第一天进度
材料采购
元器件 | number |
---|---|
MAX7219点阵模块 | ¥75.4 |
STM32F103C8T6最小系统开发板 | ¥14.5 |
JD3-33蓝牙模块 | ¥6.0 |
DHT11温湿度传感器 | ¥5.70 |
硅胶线30AWG | ¥7.0 |
这些常用的元器件在某宝上就可以轻松找得到
1.新建KEIL工程
- 先使用STM32CubeMX配置好基本时钟驱动,引脚驱动。
注意:MAX7219驱动不仅可以使用GPIO直接驱动,还可以使用SPI进行更高的 速率驱动。这里使用哪个都可以。 - 然后再配置好RTC实时时钟。这里时钟源的配置需要注意一下:
选择外部晶振可能会不稳定,最终选用内部时钟源驱动。
- 最后生成KEIL5的工程文件。
2.软件编写
其他模块的驱动就不用多说了。
/**
* @brief 汉字循环滚动显示
* @param
*/
void roll_show_hz() {
char time_buff[3];
char hour_buf[4];
char min_nuf[3];
char sec_buf[2];
char mon_buf[1];
char month[1]; //字符串缓存区
uint8_t times[4] ;
uint8_t step;
u8 i = 0;
int sx = 64;
const char buc[] = "123456";
char buf[] = "a_b_c_e_f_g_h_i_"; //欢迎新生
char buf0[] = "--j_k_l_e_m_n_o_p_q_r_"; //欢送老生
u8 temp;
u8 humidity=0;
char temp_buf[1];
char humidity_buf[1];
u8 buf_temp[] = "z_a-b-c-c-d-"; //当前天气气温
step = 0; //显示的步数
while (1) {
i++;
if (i % 20 == 0) {
Get_RTC_data(GetData, GetTime); //获取系统实时时钟
temp = leddz_struct.temputure;
humidity=leddz_struct.humidity; //获取湿度
times[0] = GetTime.Hours;
times[1] = GetTime.Minutes;
times[2] = GetTime.Seconds;
if(!GetData.WeekDay){
times[3] = 7;
}else{
times[3]=GetData.WeekDay;
}
sprintf(hour_buf, ":%2d:",times[0]);
sprintf(month, "%d", times[3]);
sprintf(min_nuf, "%2d:", times[1]);
sprintf(sec_buf, "%2d", times[2]);
if (!step) {
leddz_clear();
leddz_show_fonthz_ex(sx, 0, buf);
leddz_show_reflash();
sx--;
if (sx <=-17*8) {
step = 1;
sx = 16;
};
}
if (step == 1) {
leddz_clear();
char _23 = GetData.Year;
char date = GetData.Month;
char day = GetData.Date;
char num0[4], num1[2], num2[2];
sprintf(num0, "20%d", _23);
leddz_show_string(sx, 0, num0, 16);
leddz_show_fonthz_ex(sx + 8 * 4, 0, "s_"); //年
sprintf(num1, "%d", date);
leddz_show_string(sx + 8 * 6, 0, num1, 16);
leddz_show_fonthz_ex(sx + 8 * 7, 0, "t_"); //月
sprintf(num2, "%d", day);
leddz_show_string(sx + 8 * 9, 0, num2, 16);
leddz_show_fonthz_ex(sx + 8 * 11, 0, "u_"); //日
leddz_show_reflash(); sx--;
if (sx <= -13*8) { step = 2; sx = 64;
};
}
if(step==2){
leddz_clear();
leddz_show_fonthz_ex(sx , 0, "z_a-b-c-c-d-"); //当前天气气温
sprintf(temp_buf, "%d", temp);
leddz_show_string(sx + 8 * 12, 0, temp_buf, 16);
leddz_show_fonthz_ex(sx + 8 * 14, 0, "e-"); //度
leddz_show_fonthz_ex(sx+8*16, 0, "s-e-"); //添加湿度汉字
sprintf(humidity_buf, "%d", humidity);
leddz_show_string(sx + 8 *20,0, humidity_buf, 16);
leddz_show_string(sx + 8 * 22, 0, "%RH", 16);
//度%RH
leddz_show_reflash(); sx--;
if (sx <= -22*8) { step = 3; sx = 64;
};
}
if (step == 3) {
leddz_clear();
leddz_show_fonthz_ex(sx, 0, "v_w_"); //时间
leddz_show_string(sx + 8 *4, 0, hour_buf, 16);
leddz_show_string(sx + 8 * 8, 0, min_nuf, 16);
leddz_show_string(sx + 8 * 11, 0, sec_buf, 16);
leddz_show_fonthz_ex(sx + 8 * 13, 0, "x_y_"); //星期
leddz_show_string(sx + 8 * 17, 0, month, 16);
leddz_show_reflash();
sx--;
if (sx <= -20*8) {
step = 4;
sx = 64;
};
}
if (step == 4) {
leddz_clear();
leddz_show_fonthz_ex(sx, 0, buf0); //恭喜顺利毕业
leddz_show_reflash();
sx--;
if (sx <= -23*8) {
step = 5;
sx = 64;
};
}
if (step == 5) {
leddz_clear();
char _23 = GetData.Year;
char date = GetData.Month;
char day = GetData.Date;
char num0[4], num1[2], num2[2];
sprintf(num0, "20%d", _23);
leddz_show_string(sx, 0, num0, 16);
leddz_show_fonthz_ex(sx + 8 * 4, 0, "s_"); //年
sprintf(num1, "%d", date);
leddz_show_string(sx + 8 * 6, 0, num1, 16);
leddz_show_fonthz_ex(sx + 8 * 7, 0, "t_"); //月
sprintf(num2, "%d", day);
leddz_show_string(sx + 8 * 9, 0, num2, 16);
leddz_show_fonthz_ex(sx + 8 * 11, 0, "u_"); //日
leddz_show_reflash();
sx--;
if (sx <= -13*8) {
step =6;
sx = 64;
};
}
if(step==6){
leddz_clear();
leddz_show_fonthz_ex(sx , 0, "z_a-b-c-c-d-"); //当前天气气温
sprintf(temp_buf, "%d", temp);
leddz_show_string(sx + 8 * 12, 0, temp_buf, 16);
leddz_show_fonthz_ex(sx + 8 * 14, 0, "e-"); //度
leddz_show_fonthz_ex(sx+8*16, 0, "s-e-"); //添加湿度汉字
sprintf(humidity_buf, "%d", humidity);
leddz_show_string(sx + 8 *20,0, humidity_buf, 16);
leddz_show_string(sx + 8 * 22, 0, "%RH", 16);
//度%RH
leddz_show_reflash(); sx--;
if (sx <= -22*8) { step = 7; sx = 64;
};
}
if(step==7){
leddz_clear();
leddz_show_fonthz_ex(sx, 0, "v_w_"); //时间
leddz_show_string(sx + 8 *4, 0, hour_buf, 16);
leddz_show_string(sx + 8 * 8, 0, min_nuf, 16);
leddz_show_string(sx + 8 * 11, 0, sec_buf, 16);
leddz_show_fonthz_ex(sx + 8 * 13, 0, "x_y_"); //星期
leddz_show_string(sx + 8 * 17, 0, month, 16);
leddz_show_reflash(); sx--;
if (sx <= -18*8) {
step = 0;
sx = 64;
};
}
i = 0;
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
}
delay_ms(1);
}
}
- 程序很简单,就是堆上去的!
- 时间的的更改,为了保证当前的时间是准确的,肯定要能够调节时间,不然每次上一次程序断电后就不准了!
之前元器件采用到了蓝牙模块,那就派上了用场了。直接接收手机端蓝牙调试器发送过来的数据进行读取,然后就能够调节时间了!
串口就配置成正常的异步通讯就行,我这里采用了DMA接收。
/**
* @brief 串口重定向打印数据
* @param ch
* @param f
* @return
*/
int fputc(int ch, FILE* f) {
HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 0xffff);
return ch;
}
RTC_DateTypeDef SetData; //设置日期结构体
RTC_TimeTypeDef SetTime; //设置时间结构体
uint8_t buff[12];
char Data_data[14];
// DMA加串口空闲中断
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if(huart->Instance == USART1)
{
static uint8_t i=0;
char buff_data[1];
HAL_UARTEx_ReceiveToIdle_DMA(&huart1,buff,sizeof(buff));//因为是normal模式再次开启DMA的空闲中断接受
sprintf(buff_data,"%c",buff[0]);
Data_data[i++]=atoi(buff_data);
if(buff[0]==0x72){
i=0;
for(uint8_t j=0;j<sizeof(Data_data);j++){
Data_data[j]=0;
}
}
SetData.Year=Data_data[0]*1000+Data_data[1]*100+Data_data[2]*10+Data_data[3];
SetData.Month=Data_data[4]*10+Data_data[5];
SetData.Date=Data_data[6]*10+Data_data[7];
SetTime.Hours=Data_data[8]*10+Data_data[9];
SetTime.Minutes=Data_data[10]*10+Data_data[11];
SetTime.Seconds=Data_data[12]*10+Data_data[13];
if(i>sizeof(Data_data)-1){
Set_RTC_data(&SetData,&SetTime);
printf("setting sucessfully!");
step=0;
i=0;
}
}
HAL_UARTEx_ReceiveToIdle_DMA(&huart1,buff,sizeof(buff)); //串口1开启DMA接受
}
-
通信协议的话直接采用例子:20200402122156 就是2020年4月2日12点21分56秒 stm32 会自动计算出星期几。
-
R就是复位按键
-
注意:这里采用了atoi()函数将字符串转换为变量的值。由于手机端的串口调试器是将变量转换为字符串进行发送的。
-
到这里第一天的的进度就差不多了。
第二天进度
1.开始接线
- 刚好等到材料都到齐了,开始接线。
MAX7219一共有五个引脚
引脚名称 | 功能 |
---|---|
Vcc | 电源正极(5V) |
GND | 电源地 |
DIN | 数据输入端 |
CS | 片选信号端(拉低使能) |
CLK | 时钟信号 |
- 这三根信号线可以直接使用SPI来驱动
- 把这些线依次与STM32 配置的引脚相对应接好就行了。
2.开始调试
先检查每根线的波形是不是正确的,可以使用示波器观察。
- 然后接上MAX7219调试细节部分。
总结
- 此次意外接到的毕设让我在慌乱中抽出一点时间来完成,也祝愿学长顺利毕业吧!
- 后续我将编写、制作更多的小制作供大家共同学习进步,前行路上欢迎一键三连,感谢各位大佬!
最后附上源码工程:
- 百度网盘: https://pan.baidu.com/s/1J1Btn-NyuARS4HNFKl8TZQY
- 链接: ntl2