|
本文章最後由 antlu 於 2014-11-13 01:46 AM 編輯
最近持續在玩STM8S 的MCU,先前的太陽能電池控制器仍然有一些小問題,感謝 JOJOLING大的協助,找出了問題,泡沫排序出了問題,這次這個電壓紀錄當初也是因為要抓問題所以慢慢的衍生出來的玩具,其實本來是應該寫到外部的EEPROM,只是,I2C還搞不定,而UART也花了許多時間去摸索(後來發現STM的庫函數有問題),繞了一大圈最終還是用"寄存器方式"解決!!
泡沫排序法當初是想用來過濾雜訊用,沒想到反而造成問題(目前尚無解)若有先進可以幫忙解決,感激不盡!!
元件很簡單很少 STM8S_103 + LCM(1602+74HC595)好像就沒有甚麼零件了!!
/* 20141109 發現 系統提供的 stm8s_uart1.c 有問題無法完成編 譯經過下載網路的檔案之後有改?
但是發生了記憶體溢位的問題,於是改用一般函數來執行
功能: 紀錄模式 每分鐘紀錄一電壓值,並存入EEPROM 總數320個資料(STM8S103 640點)
紀錄模式時顯示現在的電壓紀錄點
資料輸出模式: 每按一個鍵紀錄讀出一個值若選擇輸出到PC 則一次輸出320個資料,每按一下輸出一次
顯示: 讀出紀錄 或 送至PC(完成時顯示OK)
*/
//20131225 THIS CASE FOR 74HC595 DRIVE 8BIT LCM COMMON RS/SER EN/Latch
//20140928修改給 stm8s103f3 20pin
#include "stm8s.h"
#include "stdio.h"
#include "stm8s_tim1.h"
#include "stm8s_tim4.h"
#include "stm8s_adc1.h"
#include "stm8s_delay.h"
#include "stm8s_flash.h"//20141008
#define LCMPORT GPIOA
#define SER GPIO_PIN_1 //
#define Latch GPIO_PIN_2 //
#define Clk GPIO_PIN_3 //
#define PORT_BEEP GPIOD
#define BEEP1 GPIO_PIN_4
#define DCVoltage ADC1_CHANNEL_4
#define MENU GPIO_PIN_3//MENU PIN
#define ENTER GPIO_PIN_4 //ENTER key
#define MENUPORT GPIOC //c3 menu c4 enter
//@ 變數區
u8 displayword[16];
u16 DataADC;
u8 adc_update_flag;
u16 ADCValue[10];
u8 One_Tenth_Flag;
u8 One_Sec_Flag;
u8 One_Minute_Flag;
u8 One_Hour_Flag;
u8 One_Tenth_Count;//1/10 second
u8 One_Sec_Count;
u8 One_Minute_Count;
u8 count6;
u8 display_time;
u16 Real_Voltagea;
u8 a,b,c,d,e,f,g,h;
u16 databuffer;
u16 wbuffer_count,rbuffer_count,cbuffer_count;
u8 EEPROM_ADDRESS=0xa0;
u16 addr=0x4000;//-------20141008
u8 write_flag;
u8 page_full;
u16 read_out_count;
u16 Volt_From_EPROM;
u8 MENU_flag;
u8 Next_flag;
u16 DS_buffer_count;
u8 ENTER_flag;
//@ 函數區
void Delay_us(u16 ust);
void DelayMs(u16 mst);
void GPIOinit(void);
void Send595(unsigned char Dat);
void LCD_Write_Com(unsigned char com);
void LCD_Write_Data(char Data);
void LCD_Write_Init(char com);
void LCD_Init(void);
void LCD_Clear(void);
void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s);
void LCD_Write_Char(unsigned char x,unsigned char y,unsigned char Data);
unsigned int ADC1Set(ADC1_Channel_TypeDef ADC_Channel);
void ADConvert(void);
void DigitalFiltering(void);
void AD_Data_Update(void);
void tim1init(void);
void tim1svc(void);
void tim4init(void);
void tim4svc(void);
void Display_Menu(void);
void TransData(void);
void Get_Volt_32Data(void);
void Store_To_EEPROM(void);
char putchar(char c);//傳送rs232資料出去
void KeyScan(void);
void Delay_us(u16 ust)
{
while(ust !=0)
{
ust--;
}
}
void DelayMs(u16 mst)
{
while( mst !=0)
{
Delay_us(400);
mst--;
}
}
/*
@ define LCM drive pin function
@ define timer4 clock 學習 可以使用 示波器量取波形算出時間
*/
void GPIOinit(void)
{
GPIO_Init(LCMPORT,SER|Clk|Latch,GPIO_MODE_OUT_PP_HIGH_FAST);//for LCM driver
GPIO_Init(PORT_BEEP,BEEP1,GPIO_MODE_OUT_PP_HIGH_FAST);//D4
GPIO_Init(MENUPORT,MENU|ENTER,GPIO_MODE_IN_PU_NO_IT);//變更 sw4 sw5 成為 input
}
//*******************************************
void Send595(unsigned char Dat)
{
unsigned char i;
for (i=0;i<8;i++)
{
if((Dat & 0x80)!=0)// SER=Dat & 0x80;
{
GPIO_WriteHigh(LCMPORT,SER);
}
else
{
GPIO_WriteLow(LCMPORT,SER);
}
Dat<<=1;
GPIO_WriteHigh(LCMPORT,Clk);// Clk=1;
GPIO_WriteLow(LCMPORT,Clk);
}
/* Latch=1;//20131226 這是關鍵把他取消才能做595後續栓鎖
_nop_();
Latch=0;
_nop_();*/
}
void LCD_Write_Com(unsigned char com) // 8bit mode 20131225 for 74HC595 8bit mode
{
Send595(com);//送到 74HC595
GPIO_WriteLow(LCMPORT,SER);;//command mode
GPIO_WriteHigh(LCMPORT,Latch);;
GPIO_WriteLow(LCMPORT,Latch);;
DelayMs(1);
}
void LCD_Write_Data(char Data) // 8bit mode
{
Send595(Data);//送到 74HC595
GPIO_WriteHigh(LCMPORT,SER);;//Data mode
GPIO_WriteHigh(LCMPORT,Latch);;
GPIO_WriteLow(LCMPORT,Latch);;
DelayMs(1);
}
void LCD_Write_Init(char com) // 8bit mode
{
Send595(com);//送到 74HC595
GPIO_WriteLow(LCMPORT,SER);;//command mode
GPIO_WriteHigh(LCMPORT,Latch);;
GPIO_WriteLow(LCMPORT,Latch);;
DelayMs(1);
}
/*------------------------------------------------
initial LCM
------------------------------------------------*/
void LCD_Init(void)//20131225 8bit command
{
LCD_Write_Com(0x38); //38 --- 8bit & 2 line ,28 -- 4bit & 2 line
DelayMs(5);
LCD_Write_Com(0x38); //38 --- 8bit & 2 line ,28 -- 4bit & 2 line
DelayMs(5);
LCD_Write_Com(0x38);
DelayMs(5);
LCD_Write_Com(0x08); /*显示关闭*/
DelayMs(2);
LCD_Write_Com(0x01); /*显示清屏*/
DelayMs(2);
LCD_Write_Com(0x06); /*显示光标移动设置*/
DelayMs(5);
LCD_Write_Com(0x0C); /*显示开及光标设置*/
}
/*------------------------------------------------
LCD_Clear
------------------------------------------------*/
void LCD_Clear(void)
{
LCD_Write_Com(0x01);
DelayMs(5);
}
/*------------------------------------------------
LCD_Write_String
------------------------------------------------*/
void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s)
{
if (y == 0)
{
LCD_Write_Com(0x80 + x);
}
else
{
LCD_Write_Com(0xC0 + x);
}
while (*s)
{
LCD_Write_Data( *s);
s ++;
}
}
/*------------------------------------------------
LCD_Write_Char
------------------------------------------------*/
void LCD_Write_Char(unsigned char x,unsigned char y,unsigned char Data)
{
if (y == 0)
{
LCD_Write_Com(0x80 + x);
}
else
{
LCD_Write_Com(0xC0 + x);
}
LCD_Write_Data( Data);
}
/*
@ 輸入變數 ADC_Channel
@ 取10次AD值 ADCValue[ADCcount]
@ 進行排序過濾平均 DATA完整的6次
@ 輸出變數 ADresult
*/
unsigned int ADC1Set(ADC1_Channel_TypeDef ADC_Channel)//讀取不同的AD通道
{
u16 ADresult;
{
ADC1_Init(ADC1_CONVERSIONMODE_CONTINUOUS,ADC_Channel,ADC1_PRESSEL_FCPU_D2,ADC1_EXTTRIG_TIM,DISABLE,ADC1_ALIGN_RIGHT,ADC1_SCHMITTTRIG_CHANNEL0,DISABLE);
ADConvert();
DigitalFiltering();
ADresult=DataADC;
DataADC=0;
adc_update_flag=0;
}
return ADresult;
}
/* @ 進行ADC 10次把資料存在 ADCValue[ADCcount]之中作為過濾使用
@ 變數 ADCcount
@ 陣列 ADCValue[ADCcount]
*/
void ADConvert(void)
{
unsigned char ADCcount =0;
ADC1->CR1|=0x02;
ADC1_StartConversion();//ADC開始轉換
while(ADCcount<10)
{
while(ADC1_GetFlagStatus(ADC1_FLAG_EOC)==RESET);
ADC1_ClearFlag(ADC1_FLAG_AWS0);
ADCValue[ADCcount]=ADC1_GetConversionValue();
ADCcount++;
}
ADC1->CR1 &= ~0x02;
}
/*
@ 先進行排序把頭尾2個資料去掉如此AD資料就不會亂跳電視會降速度
@ 變數 DataADC
*/
void DigitalFiltering(void)
{
unsigned char i,j;
unsigned char cptemp;
/*
for(i=10;i>=1;i--)
{
for(j=0;j<(i-1);j++)
{
if(ADCValue[j]>ADCValue[j+1])
{
cptemp=ADCValue[j];
ADCValue[j]=ADCValue[j+1];
ADCValue[j+1]=cptemp;
}
}
}
*/
DataADC=0;
for(i=2;i<=7;i++)
{
DataADC +=ADCValue;
}
DataADC /=6;
}
/*
@把電壓ad port讀入並且換算成電壓值
@ Voltage_AD_count 可以變成參數
@ Real_Voltagea實際換算的電壓
@
*/
void AD_Data_Update(void)
{
u16 Voltage_AD_count;
Voltage_AD_count = ADC1Set(DCVoltage);
Real_Voltagea=(unsigned int)((unsigned long)Voltage_AD_count*3);//1023/1023);//電壓ok 顯示12.0v
// Real_Voltagea = Voltage_AD_count;
}
/*
@Timer1 2M/20=100khz 除10000次=10hz
@
*/
void tim1init(void)//1秒1計數
{
TIM1_TimeBaseInit(19,TIM1_COUNTERMODE_UP,10000,0);//2M/20=100khz 除10000次=10hz
TIM1_ITConfig(TIM1_IT_UPDATE,ENABLE);
TIM1_GenerateEvent(TIM1_EVENTSOURCE_UPDATE);
TIM1_Cmd(ENABLE);
}
/*
@Timer1 2M/20=100khz 除10000次=10hz
@ //-------1/10 秒 作一次的工作 One_Tenth_Flag --> AD轉換
@ //-------1秒 作一次的工作 One_Sec_Flag --> 計秒 PWM 開關
@ //-------1分鐘作一次的工作 One_Minute_Flag --> 倒數計時關 斷MOS_OUTPUT 檢查日落
@ //-------1小時作一次的工作 One_Hour_Flag --> 計時
@ 1/10秒更新一次旗標 adc_update_flag //ADC轉換一次以避免過度忙碌
@
*/
void tim1svc(void)//1/10秒1計數
{
One_Tenth_Flag=1;
One_Tenth_Count++;
if(One_Tenth_Count == 10)
{
// adc_update_flag=1;//ADC轉換一次以避免過度忙碌
One_Sec_Flag=1;
One_Tenth_Count=0;
One_Sec_Count++;
if(One_Sec_Count == 60)//----------------60
{
adc_update_flag=1;//一分鐘ADC轉換一次以避免過度忙碌
One_Sec_Count=0;
One_Minute_Flag=1;
One_Minute_Count++;
if(One_Minute_Count==60)
{
One_Minute_Count=0;
One_Hour_Flag=1;
}
}
}
TIM1_ClearFlag(TIM1_FLAG_UPDATE);
}
/*
@Timer4 8bit 計時器先前用於7段掃描顯示
@
*/
void tim4init(void)
{
TIM4_TimeBaseInit(TIM4_PRESCALER_32,250);//4.05ms
TIM4_SetCounter(250);
TIM4_ITConfig(TIM4_IT_UPDATE,ENABLE);//2Mhz/8/250
TIM4_Cmd(ENABLE);
}
/*
@Timer4 8bit 計時器先前用於7段掃描顯示
@ 更改為 lcm 顯示
@ 輸出波形以確認 計時器的時間
@ 輸出腳位 GPIOD PIN4
*/
void tim4svc(void)
{
display_time++;
if(display_time==20)//10us
{
TransData();//110uS
Display_Menu();//98.84ms on, 79.56ms off=178ms
display_time=0;
}
count6++;
if (count6==5)//250>>25
{
KeyScan();
count6=0;
GPIO_WriteReverse(PORT_BEEP,BEEP1);
}
TIM4_ClearITPendingBit(TIM4_IT_UPDATE);
}
/*
@顯示lcm資料 切換旗標 reverse_display_flag
@顯示變數 a b c d e f
*/
void Display_Menu(void)
{
displayword[0]='V';
displayword[1]='o';
displayword[2]='l';
displayword[3]= 't';
displayword[4]= ':';
displayword[5]= ' ';
displayword[6]= a+'0';
displayword[7]= b+'0';
displayword[8]= '.';
displayword[9]= c+'0';
displayword[10]= d+'0';
if(MENU_flag==0)
displayword[11]= 'W';
else
displayword[11]= 'R';
displayword[12]= '-';
displayword[13]= h+'0';
displayword[14]= e+'0';
displayword[15]= f+'0';
LCD_Write_String(0,1, displayword);
}
void TransData(void)//顯示用的轉換
{
a=Real_Voltagea/1000;
b=(Real_Voltagea%1000)/100;
c=(Real_Voltagea%100)/10;
d=(Real_Voltagea)%10;
h=DS_buffer_count/100;
e=(DS_buffer_count%100)/10;
f=DS_buffer_count%10;
}
void Uart1_Init(void)
{
//UART1_CR1=0x00;//8bit no parity
UART1->CR1=0x00;
UART1->CR2=0x0c;//enable tx rx
UART1->CR3=0x20;//2bit stop
UART1->BRR2=0x00;//baud rate
UART1->BRR1=0x0d;
}
char putchar(char cc)
{
UART1->DR=(cc);
while(UART1->SR&0x80==0);
delay(160);
return(cc);
}
/*PC3 MENU-key PC4 ENTER-key call by timer4
*/
void KeyScan(void)//PC3 MENU-key PC4 ENTER-key
{
if(!GPIO_ReadInputPin(MENUPORT,MENU))//PC3 MENU-key press
{
delay(50);
if(!GPIO_ReadInputPin(MENUPORT,MENU))
{
while(!GPIO_ReadInputPin(MENUPORT,MENU));//wait sw release
MENU_flag++;
if(MENU_flag>3)
{
MENU_flag=0;
}
}
}
if(GPIO_ReadInputPin(MENUPORT,ENTER)==0)//PC3 MENU-key press
{
delay(50);
if(!GPIO_ReadInputPin(MENUPORT,ENTER))
{
while(!GPIO_ReadInputPin(MENUPORT,ENTER));//wait sw release
ENTER_flag=1;
}
}
}
void Get_Volt_Data(void)//取代void Get_Volt_32Data(void)一次一個data
{
if(adc_update_flag==1)//若放在1分鐘則每分鐘紀錄1次
{
AD_Data_Update();//940us
databuffer=Real_Voltagea;
wbuffer_count++;
putchar((u8)wbuffer_count);
}
write_flag=1;//改成每一秒就寫一次1組資料
}
void MENU_0_AD_write (void)//copy form Store_To_EEPROM()
{
/*write_flag 寫入完成旗標
databuffer[0] adc存入的資料
TrData 16bit的 欲寫入資料緩衝
wbuffer_count 寫入的計數器
Whigh_Data,Wlow_Data 寫入eprom的緩衝8bit
*/
u16 TrData;
u8 Whigh_Data,Wlow_Data;
FLASH_Unlock(FLASH_MEMTYPE_DATA);
TrData=databuffer;
Whigh_Data=(u8)(TrData>>8);
Wlow_Data=(u8)TrData;
FLASH_ProgramByte((addr+(wbuffer_count)*2),Whigh_Data);
FLASH_ProgramByte((addr+(wbuffer_count)*2+1),Wlow_Data);
if(wbuffer_count>320)
wbuffer_count=0;
write_flag=0;//寫入完成
}
void MENU_1_CLR_EPROM (void)
{
u16 cbuffer_count;
for(cbuffer_count=0;cbuffer_count<640;cbuffer_count++)
{
FLASH_EraseByte(addr+(cbuffer_count));
}
DS_buffer_count=cbuffer_count;
}
/*
讀取 eeprom的資料copy自 eprom read
Next_Flag 提取下一個data的操作
rbuffer_count 讀取資料的計數值
*/
void MENU_2_READ_EPROM (void)//
{
u8 rhigh_Data,rlow_Data;
if(Next_flag==1)
{
Next_flag=0;
rhigh_Data=FLASH_ReadByte(addr+(rbuffer_count)*2);
rlow_Data=FLASH_ReadByte(addr+(rbuffer_count)*2+1);
Volt_From_EPROM=(u16)rhigh_Data;
Volt_From_EPROM <<=8;
Volt_From_EPROM +=(u16) rlow_Data;
rbuffer_count++;
if(rbuffer_count>320)
{
rbuffer_count=0;
}
}
}
void MENU_3_Upload_PC (void)
{//read byte
u8 high_Data,low_Data;
// int *dataEprom;
for(rbuffer_count=0;rbuffer_count<320;rbuffer_count++)
{
high_Data=FLASH_ReadByte(addr+(rbuffer_count)*2);
low_Data=FLASH_ReadByte(addr+(rbuffer_count)*2+1);
putchar(high_Data);//
putchar(low_Data);//
DS_buffer_count=rbuffer_count;
}
}
void main(void)
{
u8 da=0;
GPIOinit();//顯示用
tim1init();
tim4init();
rim();
GPIO_WriteLow(LCMPORT,Latch);;
GPIO_WriteLow(LCMPORT,SER);;
DelayMs(100);
LCD_Init();
LCD_Clear();
Uart1_Init();
while(1)
{
switch(MENU_flag)//顯示用
{
case 0: LCD_Write_String(0,0, "Write Data EPROM"); //顯示紀錄data
if(ENTER_flag)
{
wbuffer_count=0;
ENTER_flag=0;
}
Get_Volt_Data();//紀錄data
MENU_0_AD_write ();
Real_Voltagea= databuffer; //顯示目前電壓值
DS_buffer_count= wbuffer_count; //顯示目前寫入位址
break;
case 1: LCD_Write_String(0,0, "CLEAR EPROM DATA "); //清除紀錄data
wbuffer_count=0;
if(ENTER_flag)
{
MENU_1_CLR_EPROM ();
ENTER_flag=0;
}
DS_buffer_count=cbuffer_count;
break;
case 2: LCD_Write_String(0,0, "READ EPROM DATA ");
if(ENTER_flag)
{
Next_flag=1;
MENU_2_READ_EPROM();
ENTER_flag=0;
putchar((u8)rbuffer_count);
}
Real_Voltagea= Volt_From_EPROM;
DS_buffer_count=rbuffer_count;
//紀錄讀出
break;
case 3CD_Write_String(0,0, " UPLOAD to PC ");//data 上傳pc
if(ENTER_flag)
{
MENU_3_Upload_PC ();
ENTER_flag=0;
}
DS_buffer_count=rbuffer_count;
break;
default:break;
}
}
}
@far @interrupt void TIM1_OVF_IRQ(void)
{
tim1svc();
}
@far @interrupt void TIM4_OVF_IRQ(void)
{
tim4svc();
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval : None
*/
void assert_failed(u8* file, u32 line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* Infinite loop */
while (1)
{
}
}
#endif
/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
|
評分
-
7
查看全部評分
-
|