ตอนนี้ผมกำลังหาทางแสดงค่า Interrupt บนจอ LCD อยู่ครับ เนื่องจากผมใช้ ADC ทำการแซมปลิ้งคลื่นรูป sine ที่มีความถี่ 50Hz ด้วยความถี่ 50MHz (20us) ทีนี้ผมต้องการทราบว่า ADC ของผมนั้น ทำการแซมปลิ้งค่าทั้งหมด 1000 ค่าจริงหรือเปล่า ซึ่งค่าที่ได้นั้นต้องการนำไปบันทึกใน array ค่าหนึ่ง ซึ่งต้องการบันทึกทั้งหมด 10000 ค่า เพื่อนำไปหาค่า RMS ไม่ทราบว่าใครพอจะมีไอเดียไหมครับ ขอบคุณครับ ทีนี้ไม่แน่ใจว่าต้องสร้างฟังก์ชั่น interrupt เพิ่มเติมเพื่อทำการบันทึกค่าหรือเปล่าครับ
ผมมองว่าน่าจะต้อง detect zoro crossing ให้ได้ก่อนหรือป่าวครับ
พอดีฟังก์ชั่นการเขียนข้อมูลของ LCD มันมีดีเลย์ด้วยครับ ทีน้จะทำอย่างไรดีที่จะให้สามารถแสดงผลบนจอ LCD โดยไม่กระทบต่อการทำงานครับ
LCD ควรอยู่ที่ main ครับ จับค่าที่ต้องการแสดงลง buffer แล้ว main ทำการดึงค่านั้นออกมา display
ตอนนี้แก้ไขเรื่อง LCD ติดดีเลได้แล้วครับ แต่ตอนนี้ผมต้องการนับจำนวนครั้งการแซมปลิ้ง -> แสดงผล และเอาค่าที่แซมปลิ้งได้ทุกค่า ทุกๆ 20us มาบันทึกลงใน array แต่ละค่า เป็นจำนวน 10000 ค่าครับ ซึ่งขั้นต้นผมใช้การ count N ใน main loop แต่ดูเหมือนจะใช้แทนค่าจำนวนการแซมปลิ้งไม่ได้ครับ เพราะผมต้องบันทึกค่าลงใน array แล้วคำนวนค่า rms ซึ่งการนับ N ในเมนลูปทำให้การบันทึกค่าไม่สอดคล้องกับการแซมปลิ้ง ค่า rms ที่ได้เลยเพี้ยนบานเลยครับ ไม่ทราบว่าใครพอจะมีไอเดียในการจับค่าของการแซมปลิ้งทีละครั้งบ้างไหมครับ
ต้องเก็บตอน sampling เลยครับ
{
Interrupt_Routine()
{
output_array[index++] = sampling();
}
...
...
main()
{
...
...
while(index < 10000)
{}
// ถ้าโปรแกรมทำงานถึงตรงนี้ แสดงว่า sampling ได้แล้ว 10000 ค่า
...
}
ถ้าตามที่ถามมา มันไม่เห็นจำเป็นที่จะต้องแสดงค่า N ทีละค่า แต่แสดงแค่ว่าได้ครบทุกค่าก็ใช้ได้แล้ว
แต่ถ้าเป็นผม ผมจะทดสอบการทำงานทีละส่วนก่อน เช่นทดสอบ Interrupt_Routine ว่าทำงานถูกต้องแน่ๆ โดยการให้มันทำงานทุกๆ วินาทีก่อนก็ได้ เมื่อทุกอย่างถูกต้อง
จึงค่อยปรับค่าระยะเวลาการอินเตอร์รัพให้เร็วขึ้นจากทุกๆ 1 วินาทีขึ้นมาเป็นทุกๆ 20 uS ซึ่งสามารถทำได้ง่ายๆโดยการปรับค่า timer
Quote from: pa_ul on February 18, 2013, 07:37:21 AM
{
Interrupt_Routine()
{
output_array[index++] = sampling();
}
...
...
main()
{
...
...
while(index < 10000)
{}
// ถ้าโปรแกรมทำงานถึงตรงนี้ แสดงว่า sampling ได้แล้ว 10000 ค่า
...
}
ถ้าตามที่ถามมา มันไม่เห็นจำเป็นที่จะต้องแสดงค่า N ทีละค่า แต่แสดงแค่ว่าได้ครบทุกค่าก็ใช้ได้แล้ว
แต่ถ้าเป็นผม ผมจะทดสอบการทำงานทีละส่วนก่อน เช่นทดสอบ Interrupt_Routine ว่าทำงานถูกต้องแน่ๆ โดยการให้มันทำงานทุกๆ วินาทีก่อนก็ได้ เมื่อทุกอย่างถูกต้อง
จึงค่อยปรับค่าระยะเวลาการอินเตอร์รัพให้เร็วขึ้นจากทุกๆ 1 วินาทีขึ้นมาเป็นทุกๆ 20 uS ซึ่งสามารถทำได้ง่ายๆโดยการปรับค่า timer
ตอนนี้ผมใช้แค่ Interrupt TIM3 เป็นตัวกำหนดการทำงานอยู่ครับ ไม่ทราบว่า
Interrupt_Routine()
{
output_array[index++] = sampling();
}
เป็นฟังก์ชั่นที่ต้องสร้างขึ้นมาใหม่หรือเป็นฟังก์ชั่นในตัว Interrupt ครับ
ท่าน Takkykun ครับ เห็นท่านถามมาหลายกระทู้แล้ว
ทุกๆท่านๆ ที่ตอบ เขาแสดง Concept ของแนวความคิดนะครับ ว่าสิ่งที่ท่านถามมีแนวทางแนะนำว่าควรทำอย่างไร ?
ถ้าเป็นรายละเอียดเรื่อง Code แนะนำให้ศึกษาเพิ่มเติมและพยายามก่อนจะดีกว่าครับ
ทำแบบนี้จะเป็นประโยชน์ต่อตัวท่านเองมากกว่า เพราะประสบการณ์เหล่านี้ท่านจะได้ด้วยตนเอง
และหากจะ share ความรู้หรือประสบการณ์ที่ท่านได้ รวมถึงความผิดพลาดที่ได้ทำ และคิดว่าเป็นประโยชน์ต่อส่วนรวม ก็ลองแสดงความเห็นได้ครับ
แต่หากติดขัดจริงๆ (ไม่ใช่แบบเบื้องต้นนักนะ มันแสดงถึงความไม่พยายามมาก่อน ) ค่อยลงคำถามต่อ จะดีกว่าครับ
ตอนนี้ผมกำลังศึกษาเรื่องของ TIM อยู่เลยครับ (ผมมือใหม่หัดเล่นครับ เพิ่งมาเขียนจริงๆ จังๆ แค่เดือนเดียวนี่เอง) ตอนนี้อยากทราบว่า การทำงานของ TIM นั้นเป็นการกำหนดสัญญาณพัลส์เพื่อทำการทริกการทำงาน ถูกไหมครับ แล้วทีนี้ผมต้องการนำสัญญาณทริกนั้นไปกำกับการทำงานของ ADC อยากทราบว่า ถ้าสมมติผมตั้งค่า TIM3 ดังคำสั่งนี้
uint16_t PrescalerValue = 0;
/**********************************************************************************************************************/
void TIM3_Configuration(void)
/**********************************************************************************************************************/
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
/* Compute the prescaler value */
PrescalerValue = (uint16_t) ((SystemCoreClock / 2) / 6000000) - 1;
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 20;
TIM_TimeBaseStructure.TIM_Prescaler = 167;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
/* Prescaler configuration */
TIM_PrescalerConfig(TIM3, PrescalerValue, TIM_PSCReloadMode_Immediate);
/* Output Compare Timing Mode configuration: Channel1 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 10;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Disable);
/* Output Compare Timing Mode configuration: Channel2 */
/*TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR2_Val;
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Disable);
/* Output Compare Timing Mode configuration: Channel3 */
/*TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR3_Val;
TIM_OC3Init(TIM3, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Disable);
/* Output Compare Timing Mode configuration: Channel4 */
/*TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR4_Val;
TIM_OC4Init(TIM3, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Disable);
/* TIM Interrupts enable */
TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE);
/* TIM3 enable counter */
TIM_Cmd(TIM3, ENABLE);
/* Connect TIM3 pins to AF2 */
GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_TIM3);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_TIM3);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource8, GPIO_AF_TIM3);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_TIM3);
}
อยากทราบว่า
1. พัลส์ของ Channel ไหนที่จะไปทริกการทำงานของ ADC ครับ
2. NVIC ต้องตั้งค่าเพื่อกำกับ TIM3 แล้วต้องตั้งค่าเพื่อกำกับ ADC และ DMA ด้วยหรือเปล่าครับ
3. ผมต้องการนับสัญญาณการทริกจาก TIM3 ไม่ทราบว่าจะมีวิธีการอย่างไรครับ เนื่องจากฟังก์ชั่น TIM ไม่ใช่ลูปของ Interrupt จึงไม่สามารถใส่ตัวแปรเพื่อทำการนับค่า count ในนั้นได้ ทีนี้ไม่ทราบว่าหากผมทำการส่งสัญญาณ Pulse ไปที่พอร์ตๆ หนึ่ง แล้วนับจังหวะที่พอร์ตนั้นส่งออกสัญญาณ pulse ซึ่งจะมีค่า logic == 1 และทำการตรวจจับโลจิคนั้นเป็นการ count หนึ่งค่า ไม่ทราบว่าพอจะเป็นไปได้ไหมครับ เพราะผมกลัวว่าการทำงานนับค่า count ซึ่งอยู่ใน main loop นั้น จะไม่ทันสัญญาณการทริก
ขอบคุณครับ
>1. พัลส์ของ Channel ไหนที่จะไปทริกการทำงานของ ADC ครับ
channel ของ ADC ที่ต้องการใช้ถูกกำหนดด้วย EXTSEL, JEXTSEL ไม่ได้อยู่ที่การตั้งค่า Timer3
>2. NVIC ต้องตั้งค่าเพื่อกำกับ TIM3 แล้วต้องตั้งค่าเพื่อกำกับ ADC และ DMA ด้วยหรือเปล่าครับ
ใช้ หรือไม่ใช้ก็ได้ อยู่ที่ความต้องการ
>3. ผมต้องการนับสัญญาณการทริกจาก TIM3 ไม่ทราบว่าจะมีวิธีการอย่างไรครับ
งงกับคำถาม ?? ถ้าตั้งให้มันทำงาน 10 ครั้งต่อวินาที มันก็เกิดขึ้น 10 ครึ้งต่อวินาที ไม่มีทางเป็นอย่างอื่นอยู่แล้ว
> เนื่องจากฟังก์ชั่น TIM ไม่ใช่ลูปของ Interrupt จึงไม่สามารถใส่ตัวแปรเพื่อทำการนับค่า count ในนั้นได้
ไปนับตรงจุดที่รับรู้การทำงานของ TIMER สิครับ
> ทีนี้ไม่ทราบว่าหากผมทำการส่งสัญญาณ Pulse ไปที่พอร์ตๆ หนึ่ง แล้วนับจังหวะที่พอร์ตนั้นส่งออกสัญญาณ pulse ซึ่งจะมีค่า logic == 1 และทำการตรวจจับโลจิคนั้นเป็นการ count
> หนึ่งค่า ไม่ทราบว่าพอจะเป็นไปได้ไหมครับ เพราะผมกลัวว่าการทำงานนับค่า count ซึ่งอยู่ใน main loop นั้น จะไม่ทันสัญญาณการทริก
ทำได้ถ้ารู้ว่าจะเอาโค้ดที่สั่งให้พอร์ตทำงานไปใส่ไว้ที่ไหน แต่ถ้ารู้ว่าจะใส่ไว้ตรงไหน ก็ไม่น่าจะมีคำถามข้อ 2 และ 3 ก่อนหน้านี้
ถามจริงๆเถอะ ว่าห่วงอะไรกับจำนวนการทริกในตอนนี้ ทั้งๆที่ยังไม่สามารถจะจัดการให้ TIMER3 ทริกให้ ADC ทำงานได้สำเร็จเลย ถ้าจัดการให้มันทำงานได้หนึ่งครั้ง แล้วจะปรับให้มันเป็นกี่ครั้งก็ไม่ใช่เรื่องยากเย็นอะไร
ต้องการหาค่า RMS ของสัญญาณ Sine ความถี่ 50 Hz ใช่มั้ยครับ
ตอนนี้อย่าเพิ่งไปสนใจ สัญญาณ Input ครับ เอาเป็นว่า Config ยังไงให้ ADC ใช้งานได้ก่อน
Interrupt ยังไม่ต้องใช้ครับ เอา Polling ให้รอดก่อน
เริ่มจาก กำหนด CLK ให้ ADC ก่อนครับ
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
เสร็จแล้วก็ Enable Clock ให้กับ ADC และ PORT ซะ
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOC, ENABLE);
Config Port ให้เป็น Analog Input
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOC, &GPIO_InitStructure);
Config ค่าต่างๆให้ ADC
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
แล้วก็ลองอ่านค่า ADC มาโชว์ดูครับ โชว์ข้อมูลดิบๆก่อน ลองหา R ปรับค่าได้ มาลองต่อ เป็น Voltage Divider ดูก่อน
While(1)
{
ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 1, ADC_SampleTime_239Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
ADC_RAW_Value = ADC_GetConversionValue(ADC1);
}
LCD SHOW ทุก 20us ไม่ได้ครับ มันเร็วไปจนตามนุษย์จับไม่ทัด
ผมมีตัวอย่างคร่าวๆในการนำไปประยุตได้บ้างลองศึกาดูครับ
ต้องการที่แซมปิ้ง 20uS ไปแก้เวลาที่ Tim3->PSC และ Tim3->ARR ครับ จัดให้เวลาเท่ากับ 20uS
ตัวอย่างเก็บค่าแค่ 2500ค่า ระวังเรืองสเต๊กพอยเตอร์ให้ดีครับ ศึกษาการดึงค่าและจัดเก็บให้ดีๆ จะพลาดกันง่ายๆ
#include"stm32f10x.h"
int bn[2500];
int in = 0;
void delay_ms(unsigned int in)
{
unsigned int jn,kn;
for(jn=0;jn<in;jn++)
for(kn=0;kn<6000;kn++);
}
void sys_clock()
{
RCC->CFGR = 0;
RCC->CR = RCC_CR_HSION;
while((RCC->CR & RCC_CR_HSIRDY)==0);
RCC->CR |= RCC_CR_HSEON;
while((RCC->CR & RCC_CR_HSERDY)==0);
RCC->CFGR = RCC_CFGR_SW_PLL|RCC_CFGR_PPRE1_DIV2|RCC_CFGR_ADCPRE_DIV6|
RCC_CFGR_PLLSRC|RCC_CFGR_PLLMULL9;
RCC->CR |= RCC_CR_PLLON;
while((RCC->CR & RCC_CR_PLLRDY)==0);
}
void ADC()
{
//ADC configuration
RCC->APB2ENR = RCC_APB2ENR_ADC1EN;
ADC1->CR1 = ADC_CR1_SCAN;
ADC1->CR2 = ADC_CR2_ADON;
ADC1->CR2 |= ADC_CR2_EXTSEL|ADC_CR2_SWSTART|ADC_CR2_EXTTRIG|ADC_CR2_CAL;//
while((ADC1->CR2 & ADC_CR2_CAL) != 0x00);/* Check the end of ADC1 calibration */
ADC1->SMPR1 = 0; // 1.5 cycles conversion time
ADC1->SQR3 = 14; //channel number 14 PC4 first conversion
}
void Time()
{
// Timer 3 counter
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
TIM3->PSC = 500; // Set prescaler (PSC + 1)
TIM3->ARR = 50; // Auto reload value 50
TIM3->DIER = TIM_DIER_UIE; // Enable update interrupt (timer level)
TIM3->CR1 = TIM_CR1_CEN; // Enable timer
}
void ADC_Count()
{
if(in<2500)
{
ADC1->CR2 |= ADC_CR2_SWSTART|ADC_CR2_ADON;/* Start ADC1 Software Conversion */
while((ADC1->SR & ADC_SR_EOC )==0)
ADC1->SR = 0;
bn[in] = ADC1->DR;
in = in+1;
}
else
TIM3->CR1 = 0; // Stop timer End Tim3
}
int main()
{
sys_clock(); // Initial clock
ADC();// Initial ADC CH 14
Time();
while (1)
{
/* code program LCD show
if(in>=2500)
{
code RMS 2500 Point
code program LCD show
}
goto keep 2500 Point new Or End Program
*/
}
}
void TIM3_IRQHandler()
{
TIM3->SR = 0;
ADC_Count();
}
อย่างที่บอกครับ LCD ควรใช้ main software ในการควบคุม interrupt จับค่า sampling ใส่ buffer แล้วนำมาให้ main ทำการ display