STM32F проект часов с индикацией на модуле TM1638.
Ссылка на видео в YouTube Прошу удержатся от высказываний, что использовать данный контролер для часов это расточительство и в нем есть готовый модуль для часов, а не использовать TIM1, суть статьи сопряжение и передача информации, а для чего использовать контролер и модуль будете решать сами.
Тут я не буду описывать, как работает чип TM1638, потому что есть похожая статья для STM8S и там есть нужная информация, вот ссылка как работает TM1638, так же в интернете много информации по этой микросхеме.
Первым делом нам нужно подключить 5V модуль к 3.3V контролеру(STM32F105RB), разработчик предусмотрел данную трудность и создал некоторые выводы толерантными к 5V, помеченные в описании контролера как FT.
Это означает, что эти GPIO выдерживают 5V но должны быть настроены только как Input floating, Output open-drain или Alternate function open-drain другие варианты могут вывести контролер из строя так как 5V попадет в его 3.3V питание.
С толерантностью разобрались, теперь давайте подключим к контролеру наш модуль, из описания контролера находим нужный нам SPI с ножками FT, им будет SPI2, схема подключения ниже.
Теперь о программе, в ней находятся для работы с модулем три основные функции:
void send_command(uint8_t com)-отправляет 8бит(команду);
void send_data(uint8_t *send_data_display)-отправляет массив из 16 данных для отображения;
uint8_t scan_button(void)-отправляет команду модулю ,ждет возвращения 4байта данных, объединяет их в байт путем битового сдвига и возвращает байт результат;
и еще одна функция:
void display_data(uint32_t multiplier)-которая получает на вход счет времени из секунд, выделяет часы, минуты и выводит на дисплей. Для вас она будет полезна, так как на ее основе сможете сделать свою функцию для выделения и отображение любых других данных.
#include "stm32f10x.h"
//Устанавливаем начальное время 12 часов, 12ч*60м*60с=43200
uint32_t time=43200;
//Массив наших символы для индикации 1,2,3 и так далее
uint8_t symbol[10]={63,6,91,79,102,109,125,7,127,111};
//Массив для отправки данных
uint8_t send_data_display[16];
void send_command(uint8_t com);
void send_data(uint8_t *send_data_display);
uint8_t scan_button(void);
void display_data(uint32_t multiplier);
void Delay_ms(uint32_t ms);
//Тело нашей программы
int main()
{
//Тактируемся от HSI
RCC_DeInit();
//Настраиваем GPIOВ_Pin_12 для стробирования команд как Output open-drain
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef PIN_Strob;
PIN_Strob.GPIO_Pin = GPIO_Pin_12;
PIN_Strob.GPIO_Speed = GPIO_Speed_10MHz;
PIN_Strob.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(GPIOB, &PIN_Strob);
GPIO_SetBits(GPIOB, GPIO_Pin_12);
//Настраиваем GPIOВ_Pin_13(SPI2_SCK) и GPIOВ_Pin_15(SPI2_MOSI)
как Alternate function open-drain
GPIO_InitTypeDef SPI2_SCK_MOSI;
SPI2_SCK_MOSI.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_15;
SPI2_SCK_MOSI.GPIO_Speed = GPIO_Speed_10MHz;
SPI2_SCK_MOSI.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_Init(GPIOB, &SPI2_SCK_MOSI);
//Настраиваем SPI2 как Master и передачу, прием данных по одной линии
RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE);
SPI_InitTypeDef SPI2_InitStructure;
SPI2_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
SPI2_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI2_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI2_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI2_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI2_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
SPI2_InitStructure.SPI_FirstBit = SPI_FirstBit_LSB;
SPI2_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_Init(SPI2, &SPI2_InitStructure);
SPI_Cmd(SPI2, ENABLE);
/*Настраиваем TIM1, предделитель(Prescaler)=200, счетчик таймера(Period)=40000
и того так как тактируемся от HSI частота которого 8Мгерц период(Period)
будет 1/8000000*200*40000=1 секунда*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
TIM_TimeBaseInitTypeDef TIM1_InitStructure;
TIM1_InitStructure.TIM_Period = 40000;
TIM1_InitStructure.TIM_Prescaler = 200-1;
TIM1_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM1_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM1_InitStructure.TIM_RepetitionCounter = 0x0000;
TIM_TimeBaseInit( TIM1, &TIM1_InitStructure);
TIM_ClearFlag( TIM1, TIM_FLAG_Update);
TIM_Cmd( TIM1, ENABLE);
//Основной цикл программы
while(1){
//Отправляем модулю две команды включится и установить автоматический инкремент адреса
send_command(143);
send_command(64);
//Отсылаем данные для отображения времени
display_data(time);
//Прибавляем секунду, проверяем, если отсчитал 24 часа то сбрасываем счетчик времени
time++;
if(time==86400){time=0;}
//Останавливаем цикл программы, ждем 1 секунду(переполнения TIM1)
while(!TIM_GetFlagStatus( TIM1, TIM_FLAG_Update)){}
//Сбрасываем флаг переполнения
TIM_ClearFlag( TIM1, TIM_FLAG_Update);
//Опрашиваем кнопки модуля для коррекции времени
while(scan_button()==1){if(time<82801){time+=3600;display_data(time);Delay_ms(400000);}}
while(scan_button()==2){if(time>3599){time-=3600;display_data(time);Delay_ms(400000);}}
while(scan_button()==4){if(time<86341){time+=60;display_data(time);Delay_ms(400000);}}
while(scan_button()==8){if(time>59){time-=60;display_data(time);Delay_ms(400000);}}
}
return 0;
}
//Функция отправки команды
void send_command(uint8_t com){
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
SPI_I2S_SendData(SPI2, com);
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) == SET);
GPIO_SetBits(GPIOB, GPIO_Pin_12);
}
//Функция отправки данных(массив) для отображения времени
void send_data(uint8_t *send_data_display){
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
SPI_I2S_SendData(SPI2, 192);
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) == SET);
for( uint8_t i=0;i<16;i++){
SPI_I2S_SendData( SPI2,send_data_display[i]);
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == !SET);
}
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) == SET);
GPIO_SetBits(GPIOB, GPIO_Pin_12);
}
//Функция опрашивает кнопки и возвращаем 8бит о нажатых кнопок
uint8_t scan_button(void){
uint8_t scan_result;
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
SPI_I2S_SendData(SPI2, 66);
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) == SET);
SPI_BiDirectionalLineConfig( SPI2, SPI_Direction_Rx);
while( !SPI_I2S_GetFlagStatus( SPI2, SPI_I2S_FLAG_RXNE));
scan_result=SPI2->DR;
while( !SPI_I2S_GetFlagStatus( SPI2, SPI_I2S_FLAG_RXNE));
scan_result|=SPI2->DR<<1;
while( !SPI_I2S_GetFlagStatus( SPI2, SPI_I2S_FLAG_RXNE));
scan_result|=SPI2->DR<<2;
while( !SPI_I2S_GetFlagStatus( SPI2, SPI_I2S_FLAG_RXNE));
scan_result|=SPI2->DR<<3;
GPIO_SetBits(GPIOB, GPIO_Pin_12);
SPI_BiDirectionalLineConfig( SPI2, SPI_Direction_Tx);
return scan_result;
}
//Функция высчитывает часы, минуты, секунды и отправляет для отображения времени
void display_data(uint32_t multiplier){
send_data_display[0]=symbol[multiplier/36000];
multiplier=multiplier-(multiplier/36000*36000);
send_data_display[2]=symbol[multiplier/3600];
multiplier=multiplier-(multiplier/3600*3600);
send_data_display[6]=symbol[multiplier/600];
multiplier=multiplier-(multiplier/600*600);
send_data_display[8]=symbol[multiplier/60];
multiplier=multiplier-(multiplier/60*60);
send_data_display[12]=symbol[multiplier/10];
multiplier=multiplier-(multiplier/10*10);
send_data_display[14]=symbol[multiplier];
send_data(send_data_display);
}
//Функция пауза
void Delay_ms(uint32_t ms){ for(uint32_t i=0; i<ms; i++){}}
Опыт толерантности выводов был, достигнут путем опыта работой с ШИМ, и гибели двух STM32F105RB,
почтим их память не вставая!